workunit.cpp 478 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include <string>
  14. #include <unordered_set>
  15. #include "jlib.hpp"
  16. #include "workunit.hpp"
  17. #include "jprop.hpp"
  18. #include "jmisc.hpp"
  19. #include "jexcept.hpp"
  20. #include "jiter.ipp"
  21. #include "jptree.hpp"
  22. #include "jtime.ipp"
  23. #include "jencrypt.hpp"
  24. #include "junicode.hpp"
  25. #include "jlzw.hpp"
  26. #include "jregexp.hpp"
  27. #include "eclrtl.hpp"
  28. #include "deftype.hpp"
  29. #include <time.h>
  30. #include "mpbase.hpp"
  31. #include "daclient.hpp"
  32. #include "dadfs.hpp"
  33. #include "dafdesc.hpp"
  34. #include "dasds.hpp"
  35. #include "danqs.hpp"
  36. #include "dautils.hpp"
  37. #include "dllserver.hpp"
  38. #include "thorplugin.hpp"
  39. #include "thorhelper.hpp"
  40. #include "workflow.hpp"
  41. #include "nbcd.hpp"
  42. #include "seclib.hpp"
  43. #include "wuerror.hpp"
  44. #include "wujobq.hpp"
  45. #ifndef _CONTAINERIZED
  46. #include "environment.hpp"
  47. #endif
  48. #include "daqueue.hpp"
  49. #include "workunit.ipp"
  50. #include "digisign.hpp"
  51. #include <list>
  52. #include <string>
  53. #include <algorithm>
  54. using namespace cryptohelper;
  55. static int workUnitTraceLevel = 1;
  56. static StringBuffer &getXPath(StringBuffer &wuRoot, const char *wuid)
  57. {
  58. // MORE - can fold in the date
  59. return wuRoot.append("/WorkUnits/").append(wuid);
  60. }
  61. //To be called by eclserver, but esp etc. won't know, so we need to store it.
  62. static StringBuffer & appendLibrarySuffix(StringBuffer & suffix)
  63. {
  64. #ifdef _WIN32
  65. suffix.append("W");
  66. #else
  67. suffix.append("L");
  68. #endif
  69. #ifdef __64BIT__
  70. suffix.append("64");
  71. #else
  72. suffix.append("32");
  73. #endif
  74. return suffix;
  75. }
  76. typedef MapStringTo<bool> UniqueScopes;
  77. static void wuAccessError(const char *username, const char *action, const char *wuscope, const char *wuid, bool excpt, bool log)
  78. {
  79. StringBuffer err;
  80. err.append("Workunit Access Denied - action: ").append(action).append(" user:").append(username ? username : "<Unknown>");
  81. if (wuid)
  82. err.append(" workunit:").append(wuid);
  83. if (wuscope)
  84. err.append(" scope:").append(wuscope);
  85. //MORE - we would need more information passed in from outside if we want to make the audit message format the same as from higher level ESP calls
  86. SYSLOG(AUDIT_TYPE_ACCESS_FAILURE, err.str());
  87. if (log)
  88. LOG(MCuserError, "%s", err.str());
  89. if (excpt)
  90. throw MakeStringException(WUERR_AccessError, "%s", err.str());
  91. }
  92. static bool checkWuScopeSecAccess(const char *wuscope, ISecManager *secmgr, ISecUser *secuser, int required, const char *action, bool excpt, bool log)
  93. {
  94. if (!secmgr || !secuser)
  95. return true;
  96. bool ret = secmgr->authorizeEx(RT_WORKUNIT_SCOPE, *secuser, wuscope)>=required;
  97. if (!ret && (log || excpt))
  98. wuAccessError(secuser->getName(), action, wuscope, NULL, excpt, log);
  99. return ret;
  100. }
  101. static bool checkWuScopeListSecAccess(const char *wuscope, ISecResourceList *scopes, int required, const char *action, bool excpt, bool log)
  102. {
  103. if (!scopes)
  104. return true;
  105. bool ret=true;
  106. if (wuscope)
  107. {
  108. Owned<ISecResource> res=scopes->getResource(wuscope);
  109. if (!res || res->getAccessFlags()<required)
  110. ret=false;
  111. }
  112. else
  113. {
  114. for (int seq=0; ret && seq<scopes->count(); seq++)
  115. {
  116. ISecResource *res=scopes->queryResource(seq);
  117. if (res && res->getAccessFlags()<required)
  118. return false;
  119. }
  120. }
  121. if (!ret && (log || excpt))
  122. wuAccessError(NULL, action, wuscope, NULL, excpt, log);
  123. return ret;
  124. }
  125. static bool checkWuSecAccess(IConstWorkUnit &cw, ISecManager *secmgr, ISecUser *secuser, int required, const char *action, bool excpt, bool log)
  126. {
  127. if (!secmgr || !secuser)
  128. return true;
  129. bool ret=secmgr->authorizeEx(RT_WORKUNIT_SCOPE, *secuser, cw.queryWuScope())>=required;
  130. if (!ret && (log || excpt))
  131. {
  132. wuAccessError(secuser->getName(), action, cw.queryWuScope(), cw.queryWuid(), excpt, log);
  133. }
  134. return ret;
  135. }
  136. static bool checkWuSecAccess(const char *wuid, ISecManager *secmgr, ISecUser *secuser, int required, const char *action, bool excpt, bool log)
  137. {
  138. if (!secmgr || !secuser)
  139. return true;
  140. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  141. Owned<IConstWorkUnit> cw = factory->openWorkUnit(wuid);
  142. bool ret=secmgr->authorizeEx(RT_WORKUNIT_SCOPE, *secuser, cw->queryWuScope())>=required;
  143. if (!ret && (log || excpt))
  144. {
  145. wuAccessError(secuser->getName(), action, cw->queryWuScope(), cw->queryWuid(), excpt, log);
  146. }
  147. return ret;
  148. }
  149. void doDescheduleWorkkunit(char const * wuid)
  150. {
  151. StringBuffer xpath;
  152. xpath.append("*/*/*/");
  153. ncnameEscape(wuid, xpath);
  154. Owned<IRemoteConnection> conn = querySDS().connect("/Schedule", myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  155. if(!conn) return;
  156. Owned<IPropertyTree> root = conn->getRoot();
  157. bool more;
  158. do more = root->removeProp(xpath.str()); while(more);
  159. }
  160. //======================================================
  161. /*
  162. * Graph progress support
  163. */
  164. CWuGraphStats::CWuGraphStats(StatisticCreatorType _creatorType, const char * _creator, unsigned wfid, const char * _rootScope, unsigned _id, bool _merge)
  165. : creatorType(_creatorType), creator(_creator), id(_id), merge(_merge)
  166. {
  167. StatsScopeId graphScopeId;
  168. verifyex(graphScopeId.setScopeText(_rootScope));
  169. StatsScopeId rootScopeId(SSTworkflow,wfid);
  170. collector.setown(createStatisticsGatherer(_creatorType, _creator, rootScopeId));
  171. collector->beginScope(graphScopeId);
  172. }
  173. void CWuGraphStats::beforeDispose()
  174. {
  175. collector->endScope();
  176. StringBuffer tag;
  177. tag.append("sg").append(id);
  178. IPropertyTree &progress = queryProgressTree();
  179. if (merge && progress.hasProp(tag))
  180. {
  181. VStringBuffer statsPath("%s/Stats", tag.str());
  182. MemoryBuffer compressed;
  183. progress.getPropBin(statsPath, compressed);
  184. if (compressed.length())
  185. {
  186. MemoryBuffer serialized;
  187. decompressToBuffer(serialized, compressed);
  188. Owned<IStatisticCollection> prevCollection = createStatisticCollection(serialized);
  189. prevCollection->mergeInto(*collector);
  190. }
  191. }
  192. Owned<IStatisticCollection> stats = collector->getResult();
  193. MemoryBuffer compressed;
  194. {
  195. MemoryBuffer serialized;
  196. serializeStatisticCollection(serialized, stats);
  197. compressToBuffer(compressed, serialized.length(), serialized.toByteArray());
  198. }
  199. unsigned minActivity = 0;
  200. unsigned maxActivity = 0;
  201. stats->getMinMaxActivity(minActivity, maxActivity);
  202. //Replace the particular subgraph statistics added by this creator
  203. IPropertyTree * subgraph = progress.setPropTree(tag);
  204. subgraph->setProp("@c", queryCreatorTypeName(creatorType));
  205. subgraph->setProp("@creator", creator);
  206. subgraph->setPropInt("@minActivity", minActivity);
  207. subgraph->setPropInt("@maxActivity", maxActivity);
  208. subgraph->setPropBin("Stats", compressed.length(), compressed.toByteArray());
  209. if (!progress.getPropBool("@stats", false))
  210. progress.setPropBool("@stats", true);
  211. }
  212. IStatisticGatherer & CWuGraphStats::queryStatsBuilder()
  213. {
  214. return *collector;
  215. }
  216. class CConstGraphProgress : public CInterface, implements IConstWUGraphProgress
  217. {
  218. public:
  219. IMPLEMENT_IINTERFACE;
  220. CConstGraphProgress(const char *_wuid, const char *_graphName, IPropertyTree *_progress) : wuid(_wuid), graphName(_graphName), progress(_progress)
  221. {
  222. if (!progress)
  223. progress.setown(createPTree());
  224. formatVersion = progress->getPropInt("@format", PROGRESS_FORMAT_V);
  225. }
  226. virtual IPropertyTree * getProgressTree(bool doFormat) override
  227. {
  228. if (progress->getPropBool("@stats"))
  229. return createProcessTreeFromStats(doFormat); // Should we cache that?
  230. return LINK(progress);
  231. }
  232. virtual unsigned queryFormatVersion()
  233. {
  234. return formatVersion;
  235. }
  236. protected:
  237. CConstGraphProgress(const char *_wuid, const char *_graphName) : wuid(_wuid), graphName(_graphName)
  238. {
  239. formatVersion = PROGRESS_FORMAT_V;
  240. }
  241. static void expandStats(IPropertyTree * target, IStatisticCollection & collection, bool doFormat)
  242. {
  243. StringBuffer formattedValue;
  244. unsigned numStats = collection.getNumStatistics();
  245. for (unsigned i=0; i < numStats; i++)
  246. {
  247. StatisticKind kind;
  248. unsigned __int64 value;
  249. collection.getStatistic(kind, value, i);
  250. if (doFormat)
  251. {
  252. formatStatistic(formattedValue.clear(), value, kind);
  253. target->setProp(queryTreeTag(kind), formattedValue);
  254. }
  255. else
  256. {
  257. target->setPropInt64(queryTreeTag(kind), value);
  258. }
  259. }
  260. }
  261. void expandProcessTreeFromStats(IPropertyTree * rootTarget, IPropertyTree * target, IStatisticCollection * collection, bool doFormat)
  262. {
  263. expandStats(target, *collection, doFormat);
  264. StringBuffer scopeName;
  265. Owned<IStatisticCollectionIterator> activityIter = &collection->getScopes(NULL, false);
  266. ForEach(*activityIter)
  267. {
  268. IStatisticCollection & cur = activityIter->query();
  269. cur.getScope(scopeName.clear());
  270. const char * id = scopeName.str();
  271. const char * tag;
  272. IPropertyTree * curTarget = target;
  273. switch (cur.queryScopeType())
  274. {
  275. case SSTedge:
  276. tag = "edge";
  277. id += strlen(EdgeScopePrefix);
  278. break;
  279. case SSTactivity:
  280. tag = "node";
  281. id += strlen(ActivityScopePrefix);
  282. break;
  283. case SSTsubgraph:
  284. //All subgraphs are added a root elements in the progress tree
  285. curTarget = rootTarget;
  286. tag = "node";
  287. id += strlen(SubGraphScopePrefix);
  288. break;
  289. case SSTchildgraph:
  290. case SSTworkflow:
  291. case SSTgraph:
  292. // SSTworkflow and SSTgraph may be safely ignored. They are not required to produce the statistics.
  293. expandProcessTreeFromStats(rootTarget, target, &cur, doFormat);
  294. continue;
  295. case SSTfunction:
  296. case SSTchannel:
  297. case SSTglobal:
  298. //MORE:Should function scopes be included in the graph scope somehow, and if so how?
  299. continue;
  300. default:
  301. throwUnexpected();
  302. }
  303. IPropertyTree * next = curTarget->addPropTree(tag);
  304. next->setProp("@id", id);
  305. expandProcessTreeFromStats(rootTarget, next, &cur, doFormat);
  306. }
  307. }
  308. IPropertyTree * createProcessTreeFromStats(bool doFormat)
  309. {
  310. MemoryBuffer compressed;
  311. MemoryBuffer serialized;
  312. Owned<IPropertyTree> progressTree = createPTree();
  313. Owned<IPropertyTreeIterator> iter = progress->getElements("sg*");
  314. ForEach(*iter)
  315. {
  316. IPropertyTree & curSubGraph = iter->query();
  317. curSubGraph.getPropBin("Stats", compressed.clear());
  318. //Protect against updates that delete the stats while we are iterating
  319. if (compressed.length())
  320. {
  321. decompressToBuffer(serialized.clear(), compressed);
  322. Owned<IStatisticCollection> collection = createStatisticCollection(serialized);
  323. expandProcessTreeFromStats(progressTree, progressTree, collection, doFormat);
  324. }
  325. }
  326. return progressTree.getClear();
  327. }
  328. protected:
  329. Linked<IPropertyTree> progress;
  330. StringAttr wuid, graphName;
  331. unsigned formatVersion;
  332. };
  333. extern WORKUNIT_API IConstWUGraphProgress *createConstGraphProgress(const char *_wuid, const char *_graphName, IPropertyTree *_progress)
  334. {
  335. return new CConstGraphProgress(_wuid, _graphName, _progress);
  336. }
  337. //--------------------------------------------------------------------------------------------------------------------
  338. /*
  339. * Create a user friendly description a scope/stats combination. Only currently used for elapsed time for root subgraphs
  340. */
  341. static void createDefaultDescription(StringBuffer & description, StatisticKind kind, StatisticScopeType scopeType, const char * scope)
  342. {
  343. switch (kind)
  344. {
  345. case StTimeElapsed:
  346. {
  347. if (scopeType != SSTsubgraph)
  348. break;
  349. //Create a default description for a root subgraph
  350. const char * colon = strchr(scope, ':');
  351. if (!colon)
  352. break;
  353. const char * subgraph = colon+1;
  354. //Check for nested subgraph
  355. if (strchr(subgraph, ':'))
  356. break;
  357. assertex(strncmp(subgraph, SubGraphScopePrefix, strlen(SubGraphScopePrefix)) == 0);
  358. StringAttr graphname;
  359. graphname.set(scope, colon - scope);
  360. unsigned subId = atoi(subgraph + strlen(SubGraphScopePrefix));
  361. formatGraphTimerLabel(description, graphname, 0, subId);
  362. return;
  363. }
  364. }
  365. describeScope(description, scope);
  366. }
  367. /* Represents a single statistic */
  368. class ExtractedStatistic : public CInterfaceOf<IConstWUStatistic>
  369. {
  370. public:
  371. virtual IStringVal & getDescription(IStringVal & str, bool createDefault) const
  372. {
  373. if (!description && createDefault)
  374. {
  375. StringBuffer desc;
  376. createDefaultDescription(desc, kind, scopeType, scope);
  377. str.set(desc);
  378. return str;
  379. }
  380. str.set(description);
  381. return str;
  382. }
  383. virtual IStringVal & getCreator(IStringVal & str) const
  384. {
  385. str.set(creator);
  386. return str;
  387. }
  388. virtual const char * queryScope() const
  389. {
  390. return scope ? scope : "";
  391. }
  392. virtual IStringVal & getFormattedValue(IStringVal & str) const
  393. {
  394. StringBuffer formatted;
  395. formatStatistic(formatted, value, measure);
  396. str.set(formatted);
  397. return str;
  398. }
  399. virtual StatisticMeasure getMeasure() const
  400. {
  401. return measure;
  402. }
  403. virtual StatisticKind getKind() const
  404. {
  405. return kind;
  406. }
  407. virtual StatisticCreatorType getCreatorType() const
  408. {
  409. return creatorType;
  410. }
  411. virtual StatisticScopeType getScopeType() const
  412. {
  413. return scopeType;
  414. }
  415. virtual unsigned __int64 getValue() const
  416. {
  417. return value;
  418. }
  419. virtual unsigned __int64 getCount() const
  420. {
  421. return count;
  422. }
  423. virtual unsigned __int64 getMax() const
  424. {
  425. return max;
  426. }
  427. virtual unsigned __int64 getTimestamp() const
  428. {
  429. return timeStamp;
  430. }
  431. public:
  432. StringBuffer creator;
  433. StringBuffer description;
  434. StringBuffer scope;
  435. StatisticMeasure measure;
  436. StatisticKind kind;
  437. StatisticCreatorType creatorType;
  438. StatisticScopeType scopeType;
  439. unsigned __int64 value;
  440. unsigned __int64 count;
  441. unsigned __int64 max;
  442. unsigned __int64 timeStamp;
  443. };
  444. //---------------------------------------------------------------------------------------------------------------------
  445. /*
  446. * The following compare functions are used to ensure that comparison are consistent with
  447. * compareScopeName() in jstats. This ensures that the scope iterators from different sources
  448. * are processed in a consistent order
  449. */
  450. static int compareGraphNode(IInterface * const *ll, IInterface * const *rr)
  451. {
  452. IPropertyTree *l = (IPropertyTree *) *ll;
  453. IPropertyTree *r = (IPropertyTree *) *rr;
  454. unsigned lwfid = l->getPropInt("@wfid");
  455. unsigned rwfid = r->getPropInt("@wfid");
  456. if (lwfid != rwfid)
  457. return lwfid > rwfid ? +1 : -1;
  458. const char * lname = l->queryName();
  459. const char * rname = r->queryName();
  460. return compareScopeName(lname, rname);
  461. }
  462. static int compareSubGraphStatsNode(IInterface * const *ll, IInterface * const *rr)
  463. {
  464. IPropertyTree *l = (IPropertyTree *) *ll;
  465. IPropertyTree *r = (IPropertyTree *) *rr;
  466. return compareScopeName(l->queryName(), r->queryName());
  467. }
  468. static int compareSubGraphNode(IInterface * const *ll, IInterface * const *rr)
  469. {
  470. IPropertyTree *l = (IPropertyTree *) *ll;
  471. IPropertyTree *r = (IPropertyTree *) *rr;
  472. return l->getPropInt("@id") - r->getPropInt("@id");
  473. }
  474. static int compareActivityNode(IInterface * const *ll, IInterface * const *rr)
  475. {
  476. IPropertyTree *l = (IPropertyTree *) *ll;
  477. IPropertyTree *r = (IPropertyTree *) *rr;
  478. return l->getPropInt("@id") - r->getPropInt("@id");
  479. }
  480. static int compareEdgeNode(IInterface * const *ll, IInterface * const *rr)
  481. {
  482. IPropertyTree *l = (IPropertyTree *) *ll;
  483. IPropertyTree *r = (IPropertyTree *) *rr;
  484. //MORE: Edge needs more work
  485. const char * leftId = l->queryProp("@id");
  486. const char * rightId = r->queryProp("@id");
  487. unsigned leftAc = atoi(leftId);
  488. unsigned rightAc = atoi(rightId);
  489. if (leftAc != rightAc)
  490. return (int)(leftAc - rightAc);
  491. const char * leftSep = strchr(leftId, '_');
  492. const char * rightSep = strchr(rightId, '_');
  493. assertex(leftSep && rightSep);
  494. return atoi(leftSep+1) - atoi(rightSep+1);
  495. }
  496. //---------------------------------------------------------------------------------------------------------------------
  497. /*
  498. * A class for implementing a scope iterator that walks through graph progress information
  499. */
  500. class CConstGraphProgressScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
  501. {
  502. public:
  503. CConstGraphProgressScopeIterator(const char * wuid, const ScopeFilter & _filter, __uint64 _minVersion) : filter(_filter), minVersion(_minVersion)
  504. {
  505. //Examine the filter, and determine if we only need to look at a single graph/subgraph
  506. StringAttr singleGraph;
  507. const StringArray & scopesToMatch = filter.queryScopes();
  508. if (scopesToMatch)
  509. {
  510. bool seenGraph = false;
  511. bool seenSubGraph = false;
  512. ForEachItemIn(iScope, scopesToMatch)
  513. {
  514. StringArray ids;
  515. ids.appendList(scopesToMatch.item(iScope), ":");
  516. ForEachItemIn(i, ids)
  517. {
  518. const char * curId = ids.item(i);
  519. StatsScopeId id(curId);
  520. switch (id.queryScopeType())
  521. {
  522. case SSTgraph:
  523. if (seenGraph)
  524. {
  525. if (singleGraph && !streq(singleGraph, curId))
  526. singleGraph.clear();
  527. }
  528. else
  529. {
  530. if (!id.isWildcard())
  531. singleGraph.set(curId);
  532. seenGraph = true;
  533. }
  534. break;
  535. case SSTsubgraph:
  536. if (seenSubGraph)
  537. {
  538. if (singleSubGraph && !streq(singleSubGraph, curId))
  539. singleSubGraph.clear();
  540. }
  541. else
  542. {
  543. if (!id.isWildcard())
  544. singleSubGraph.set(curId);
  545. seenSubGraph = true;
  546. }
  547. break;
  548. }
  549. }
  550. }
  551. }
  552. rootPath.append("/GraphProgress/").append(wuid).append('/');
  553. if (singleGraph)
  554. rootPath.append(singleGraph).append("/");
  555. //Don't lock the statistics while we iterate - any partial updates must not cause problems
  556. if (daliClientActive())
  557. conn.setown(querySDS().connect(rootPath.str(), myProcessSession(), RTM_NONE, SDS_LOCK_TIMEOUT));
  558. if (conn && !singleGraph)
  559. {
  560. graphIter.setown(conn->queryRoot()->getElements("*"));
  561. graphIter.setown(createSortedIterator(*graphIter, compareGraphNode));
  562. }
  563. valid = false;
  564. }
  565. IMPLEMENT_IINTERFACE_USING(CInterfaceOf<IConstWUScopeIterator>)
  566. virtual bool first()
  567. {
  568. valid = false;
  569. if (!conn)
  570. return false;
  571. if (graphIter && !graphIter->first())
  572. return false;
  573. if (!firstSubGraph())
  574. {
  575. if (!nextGraph())
  576. return false;
  577. }
  578. valid = true;
  579. return true;
  580. }
  581. virtual bool next()
  582. {
  583. if (!nextChildScope())
  584. {
  585. if (!nextSubGraph())
  586. {
  587. if (!nextGraph())
  588. {
  589. valid = false;
  590. return false;
  591. }
  592. }
  593. }
  594. return true;
  595. }
  596. virtual bool nextSibling() override
  597. {
  598. if (collections.ordinality() == 0)
  599. return false;
  600. assertex(childIterators.ordinality() < collections.ordinality());
  601. collections.pop();
  602. //next will call childIterator.next() - walking the next sibling
  603. return next();
  604. }
  605. virtual bool nextParent() override
  606. {
  607. if (collections.ordinality() == 0)
  608. return false;
  609. assertex(childIterators.ordinality() < collections.ordinality());
  610. collections.pop();
  611. if (collections.ordinality() == 0)
  612. return false;
  613. //Finish with this node - so next will move onto the sibling of the parent node.
  614. finishCollection();
  615. return next();
  616. }
  617. virtual bool isValid()
  618. {
  619. return valid;
  620. }
  621. protected:
  622. bool firstSubGraph()
  623. {
  624. IPropertyTree & graphNode = graphIter ? graphIter->query() : *conn->queryRoot();
  625. const char * xpath = "sg*";
  626. StringBuffer childXpath;
  627. if (singleSubGraph)
  628. {
  629. childXpath.append(singleSubGraph);
  630. xpath = childXpath.str();
  631. }
  632. subgraphIter.setown(graphNode.getElements(xpath));
  633. if (subgraphIter)
  634. {
  635. if (!singleSubGraph)
  636. subgraphIter.setown(createSortedIterator(*subgraphIter, compareSubGraphStatsNode));
  637. }
  638. else
  639. subgraphIter.setown(graphNode.getElements("sg0"));
  640. if (!subgraphIter->first())
  641. return false;
  642. if (firstStat())
  643. return true;
  644. return nextSubGraph();
  645. }
  646. bool nextSubGraph()
  647. {
  648. for (;;)
  649. {
  650. if (!subgraphIter->next())
  651. return false;
  652. if (firstStat())
  653. return true;
  654. }
  655. }
  656. bool nextGraph()
  657. {
  658. if (!graphIter)
  659. return false;
  660. for (;;)
  661. {
  662. if (!graphIter->next())
  663. return false;
  664. if (firstSubGraph())
  665. return true;
  666. }
  667. }
  668. bool firstStat()
  669. {
  670. IPropertyTree & curSubGraph = subgraphIter->query();
  671. if (!checkSubGraph())
  672. return false;
  673. curSubGraph.getPropBin("Stats", compressed.clear());
  674. //Don't crash on old format progress...
  675. if (compressed.length() == 0)
  676. return false;
  677. decompressToBuffer(serialized.clear(), compressed);
  678. Owned<IStatisticCollection> collection = createStatisticCollection(serialized);
  679. statsIterator.timeStamp = collection->queryWhenCreated();
  680. if (!beginCollection(*collection))
  681. return false;
  682. // When workflow is root element, it is just a container. Ignore the workflow element here
  683. // as WorkUnitStatisticsScopeIterator will produce workflow scope - don't want duplicates.
  684. // (Note: workflow element never contains stats).
  685. if (collections.tos().queryScopeType() == SSTworkflow)
  686. {
  687. if (!next())
  688. return false;
  689. }
  690. //The root element of a collection is a graph - but it is only there to nest the subgraphs in.
  691. //Do not iterate it as a separate element - unless it has some stats.
  692. IStatisticCollection & curCollection = collections.tos();
  693. if ((curCollection.queryScopeType() != SSTgraph) || (curCollection.getNumStatistics() != 0))
  694. return true;
  695. return next();
  696. }
  697. bool beginCollection(IStatisticCollection & collection)
  698. {
  699. collections.append(OLINK(collection));
  700. curScopeType = collection.queryScopeType();
  701. collection.getFullScope(curScopeName.clear());
  702. ScopeCompare result = filter.compare(curScopeName);
  703. if (result & SCequal)
  704. return true;
  705. //If this scope cannot be the parent of a match then discard it.
  706. if (!(result & SCparent))
  707. collections.pop();
  708. //walk the next element
  709. return nextChildScope();
  710. }
  711. bool nextChildScope()
  712. {
  713. for (;;)
  714. {
  715. if (collections.ordinality() == 0)
  716. return false;
  717. IStatisticCollection * curCollection = &collections.tos();
  718. if (childIterators.ordinality() < collections.ordinality())
  719. {
  720. ScopeCompare result = filter.compare(curScopeName);
  721. //Do not walk children scopes if it is unrelated
  722. if (result & SCparent)
  723. {
  724. //Start iterating the children for the current collection
  725. childIterators.append(curCollection->getScopes(NULL, true));
  726. if (!childIterators.tos().first())
  727. {
  728. finishCollection();
  729. continue;
  730. }
  731. }
  732. else
  733. {
  734. //Don't walk the child scopes
  735. collections.pop();
  736. continue;
  737. }
  738. }
  739. else if (!childIterators.tos().next())
  740. {
  741. finishCollection();
  742. continue;
  743. }
  744. if (beginCollection(childIterators.tos().query()))
  745. return true;
  746. }
  747. }
  748. void finishCollection()
  749. {
  750. collections.pop();
  751. childIterators.pop();
  752. }
  753. bool checkSubGraph()
  754. {
  755. IPropertyTree & curSubGraph = subgraphIter->query();
  756. StatisticCreatorType creatorType = queryCreatorType(curSubGraph.queryProp("@c"), SCTnone);
  757. const char * creator = curSubGraph.queryProp("@creator");
  758. //MORE: Check minVersion and allow early filtering
  759. //MORE: Potentially filter by creator type??
  760. // if (!filter->matches(creatorType, creator, SSTall, NULL, SMeasureAll, StKindAll, AnyStatisticValue))
  761. // return false;
  762. statsIterator.creatorType = creatorType;
  763. statsIterator.creator.set(creator);
  764. return true;
  765. }
  766. virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
  767. {
  768. if ((whichProperties & PTstatistics))
  769. {
  770. /*
  771. MORE: Code from the statsIterator class should be inlined here - code will be much simpler
  772. but it currently needs an implementation of the IConstWUStatistic 3rd parameter
  773. IStatisticCollection & collection = collections.tos();
  774. ForEachItemIn(i, collection)
  775. {
  776. StatisticKind kind;
  777. unsigned __int64 value;
  778. collection->getStatistic(kind, value, i);
  779. visitor.noteStatistic(kind, value, *this);
  780. }
  781. */
  782. statsIterator.reset(curScopeName, curScopeType, collections.tos());
  783. ForEach(statsIterator)
  784. statsIterator.play(visitor);
  785. }
  786. }
  787. virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
  788. {
  789. return collections.tos().getStatistic(kind, value);
  790. }
  791. virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
  792. {
  793. return nullptr;
  794. }
  795. virtual const char * queryHint(const char * kind) const override
  796. {
  797. return nullptr;
  798. }
  799. virtual const char * queryScope() const override
  800. {
  801. return curScopeName;
  802. }
  803. virtual StatisticScopeType getScopeType() const override
  804. {
  805. return curScopeType;
  806. }
  807. private:
  808. class ScopeStatisticsIterator : public CInterfaceOf<IConstWUStatistic>
  809. {
  810. friend class CConstGraphProgressScopeIterator; // cleaner if this was removed + setScope() functions added.
  811. public:
  812. void reset(const char * _scopeName, StatisticScopeType _scopeType, IStatisticCollection & _collection)
  813. {
  814. scope.set(_scopeName);
  815. scopeType = _scopeType;
  816. collection = &_collection;
  817. numStats = collection->getNumStatistics();
  818. }
  819. // interface IConstWUStatisticIterator
  820. IConstWUStatistic & query()
  821. {
  822. return *this;
  823. }
  824. bool first()
  825. {
  826. curStatIndex = 0;
  827. if (curStatIndex >= numStats)
  828. return false;
  829. collection->getStatistic(kind, value, curStatIndex);
  830. //MORE: Allow filtering:
  831. /*
  832. * if (filter && !filter->matches(SCTall, NULL, SSTall, NULL, queryMeasure(kind), kind, value))
  833. continue;
  834. */
  835. return true;
  836. }
  837. bool next()
  838. {
  839. for (;;)
  840. {
  841. ++curStatIndex;
  842. if (curStatIndex >= numStats)
  843. return false;
  844. collection->getStatistic(kind, value, curStatIndex);
  845. //MORE: Allow stats filtering
  846. return true;
  847. }
  848. }
  849. bool isValid()
  850. {
  851. return curStatIndex < numStats;
  852. }
  853. //interface IConstWUStatistic
  854. virtual IStringVal & getDescription(IStringVal & str, bool createDefault) const override
  855. {
  856. if (createDefault)
  857. {
  858. StringBuffer description;
  859. createDefaultDescription(description, kind, scopeType, scope);
  860. str.set(description);
  861. }
  862. return str;
  863. }
  864. virtual IStringVal & getCreator(IStringVal & str) const override
  865. {
  866. str.set(creator);
  867. return str;
  868. }
  869. virtual const char * queryScope() const override
  870. {
  871. return scope;
  872. }
  873. virtual IStringVal & getFormattedValue(IStringVal & str) const override
  874. {
  875. StringBuffer formatted;
  876. formatStatistic(formatted, value, kind);
  877. str.set(formatted);
  878. return str;
  879. }
  880. virtual StatisticMeasure getMeasure() const override
  881. {
  882. return queryMeasure(kind);
  883. }
  884. virtual StatisticKind getKind() const override
  885. {
  886. return kind;
  887. }
  888. virtual StatisticCreatorType getCreatorType() const override
  889. {
  890. return creatorType;
  891. }
  892. virtual StatisticScopeType getScopeType() const override
  893. {
  894. return scopeType;
  895. }
  896. virtual unsigned __int64 getValue() const override
  897. {
  898. return value;
  899. }
  900. virtual unsigned __int64 getCount() const override
  901. {
  902. return 1;
  903. }
  904. virtual unsigned __int64 getMax() const override
  905. {
  906. return 0;
  907. }
  908. virtual unsigned __int64 getTimestamp() const override
  909. {
  910. return timeStamp;
  911. }
  912. void play(IWuScopeVisitor & visitor)
  913. {
  914. visitor.noteStatistic(kind, value, *this);
  915. }
  916. protected:
  917. IStatisticCollection * collection = nullptr;
  918. StringBuffer creator;
  919. StringBuffer scope;
  920. StatisticKind kind;
  921. StatisticCreatorType creatorType;
  922. StatisticScopeType scopeType;
  923. unsigned __int64 value = 0;
  924. unsigned __int64 timeStamp;
  925. unsigned curStatIndex = 0;
  926. unsigned numStats = 0;
  927. } statsIterator;
  928. Owned<IRemoteConnection> conn;
  929. StringBuffer curScopeName;
  930. StatisticScopeType curScopeType = SSTnone;
  931. __uint64 minVersion;
  932. const ScopeFilter & filter;
  933. StringBuffer rootPath;
  934. StringAttr singleSubGraph;
  935. Owned<IPropertyTreeIterator> graphIter;
  936. Owned<IPropertyTreeIterator> subgraphIter;
  937. IArrayOf<IStatisticCollection> collections;
  938. IArrayOf<IStatisticCollectionIterator> childIterators; // Iterator(n) through collections(n) - created once iterating children
  939. MemoryBuffer compressed;
  940. MemoryBuffer serialized;
  941. bool valid;
  942. };
  943. int compareNoteScopeOrder(IConstWUException & left, IConstWUException & right)
  944. {
  945. return compareScopeName(left.queryScope(), right.queryScope());
  946. }
  947. int compareNoteScopeOrder(IInterface * const * left, IInterface * const * right)
  948. {
  949. return compareNoteScopeOrder(*static_cast<IConstWUException *>(*left), *static_cast<IConstWUException *>(*right));
  950. }
  951. class NotesIterator : public CInterfaceOf<IConstWUScopeIterator>
  952. {
  953. public:
  954. NotesIterator(const IConstWorkUnit * wu, const ScopeFilter & _filter)
  955. {
  956. Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
  957. ForEach(*exceptions)
  958. {
  959. IConstWUException & exception = exceptions->query();
  960. if (exception.queryScope()!=nullptr)
  961. notes.append(OLINK(exception));
  962. }
  963. notes.sort(compareNoteScopeOrder);
  964. }
  965. virtual bool first() override
  966. {
  967. baseIndex = 0;
  968. return updateCurrent();
  969. }
  970. virtual bool next() override
  971. {
  972. baseIndex += numCurrentScope;
  973. return updateCurrent();
  974. }
  975. virtual bool isValid() override
  976. {
  977. return notes.isItem(baseIndex);
  978. }
  979. virtual bool nextSibling() override
  980. {
  981. //Search until the current scope is not a child of the previous scope
  982. StringBuffer savedScope(queryScope());
  983. for (;;)
  984. {
  985. if (!next())
  986. return false;
  987. if (compareScopes(queryScope(), savedScope) != SCchild)
  988. return true;
  989. }
  990. }
  991. virtual bool nextParent() override
  992. {
  993. //Search until the current scope is not a child of the previous parent scope
  994. StringBuffer parentScope;
  995. if (getParentScope(parentScope, queryScope()))
  996. {
  997. for (;;)
  998. {
  999. if (!next())
  1000. return false;
  1001. if (compareScopes(queryScope(), parentScope) != SCchild)
  1002. return true;
  1003. }
  1004. }
  1005. else
  1006. {
  1007. finish();
  1008. return false;
  1009. }
  1010. }
  1011. virtual const char * queryScope() const override
  1012. {
  1013. const char * scope = notes.item(baseIndex).queryScope();
  1014. return scope ? scope : "";
  1015. }
  1016. virtual StatisticScopeType getScopeType() const override
  1017. {
  1018. const char * tail = queryScopeTail(queryScope());
  1019. StatsScopeId id(tail);
  1020. return id.queryScopeType();
  1021. }
  1022. virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties = PTall) override
  1023. {
  1024. if (whichProperties & PTnotes)
  1025. {
  1026. for (unsigned i=0; i < numCurrentScope; i++)
  1027. {
  1028. IConstWUException & cur = notes.item(baseIndex + i);
  1029. visitor.noteException(cur);
  1030. }
  1031. }
  1032. }
  1033. virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
  1034. {
  1035. return false;
  1036. }
  1037. virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
  1038. {
  1039. return nullptr;
  1040. }
  1041. virtual const char * queryHint(const char * kind) const override
  1042. {
  1043. return nullptr;
  1044. }
  1045. private:
  1046. bool updateCurrent()
  1047. {
  1048. if (!notes.isItem(baseIndex))
  1049. return false;
  1050. // set numCurrentScope to number of notes in the current scope
  1051. unsigned next = baseIndex+1;
  1052. while (next < notes.ordinality())
  1053. {
  1054. if (compareNoteScopeOrder(notes.item(baseIndex), notes.item(next)) != 0)
  1055. break;
  1056. next++;
  1057. }
  1058. numCurrentScope = (next - baseIndex);
  1059. return true;
  1060. }
  1061. void finish()
  1062. {
  1063. baseIndex = notes.ordinality();
  1064. }
  1065. unsigned baseIndex = 0;
  1066. unsigned numCurrentScope = 0;
  1067. IArrayOf<IConstWUException> notes;
  1068. };
  1069. int compareStatisticScopes(IConstWUStatistic & left, IConstWUStatistic & right)
  1070. {
  1071. return compareScopeName(left.queryScope(), right.queryScope());
  1072. }
  1073. int compareStatisticScopes(IInterface * const * left, IInterface * const * right)
  1074. {
  1075. return compareStatisticScopes(*static_cast<IConstWUStatistic *>(*left), *static_cast<IConstWUStatistic *>(*right));
  1076. }
  1077. /*
  1078. * An implementation of IConstWUScopeIterator for global workunit statistics.
  1079. */
  1080. class WorkUnitStatisticsScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
  1081. {
  1082. public:
  1083. WorkUnitStatisticsScopeIterator(const IArrayOf<IConstWUStatistic> & _statistics, const ScopeFilter & _filter)
  1084. {
  1085. ForEachItemIn(i, _statistics)
  1086. {
  1087. IConstWUStatistic & cur = _statistics.item(i);
  1088. if (_filter.compare(cur.queryScope()) & SCequal)
  1089. statistics.append(OLINK(cur));
  1090. }
  1091. statistics.sort(compareStatisticScopes);
  1092. }
  1093. virtual bool first() override
  1094. {
  1095. curIndex = 0;
  1096. return initScope();
  1097. }
  1098. virtual bool next() override
  1099. {
  1100. curIndex += numStatistics;
  1101. return initScope();
  1102. }
  1103. virtual bool nextSibling() override
  1104. {
  1105. //Search until the current scope is not a child of the previous scope
  1106. StringBuffer savedScope(queryScope());
  1107. for (;;)
  1108. {
  1109. if (!next())
  1110. return false;
  1111. if (compareScopes(queryScope(), savedScope) != SCchild)
  1112. return true;
  1113. }
  1114. }
  1115. virtual bool nextParent() override
  1116. {
  1117. //Search until the current scope is not a child of the previous parent scope
  1118. StringBuffer parentScope;
  1119. if (getParentScope(parentScope, queryScope()))
  1120. {
  1121. for (;;)
  1122. {
  1123. if (!next())
  1124. return false;
  1125. if (compareScopes(queryScope(), parentScope) != SCchild)
  1126. return true;
  1127. }
  1128. }
  1129. else
  1130. {
  1131. finish();
  1132. return false;
  1133. }
  1134. }
  1135. virtual bool isValid() override
  1136. {
  1137. return statistics.isItem(curIndex);
  1138. }
  1139. virtual const char * queryScope() const override
  1140. {
  1141. return statistics.item(curIndex).queryScope();
  1142. }
  1143. virtual StatisticScopeType getScopeType() const override
  1144. {
  1145. return statistics.item(curIndex).getScopeType();
  1146. }
  1147. virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
  1148. {
  1149. if (whichProperties & PTstatistics)
  1150. {
  1151. for (unsigned i=0; i < numStatistics; i++)
  1152. {
  1153. IConstWUStatistic & cur = statistics.item(curIndex + i);
  1154. visitor.noteStatistic(cur.getKind(), cur.getValue(), cur);
  1155. }
  1156. }
  1157. }
  1158. virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
  1159. {
  1160. for (unsigned i=0; i < numStatistics; i++)
  1161. {
  1162. IConstWUStatistic & cur = statistics.item(curIndex + i);
  1163. if (cur.getKind() == kind)
  1164. {
  1165. value = cur.getValue();
  1166. return true;
  1167. }
  1168. }
  1169. return false;
  1170. }
  1171. virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
  1172. {
  1173. return nullptr;
  1174. }
  1175. virtual const char * queryHint(const char * kind) const
  1176. {
  1177. return nullptr;
  1178. }
  1179. protected:
  1180. inline IConstWUStatistic & queryStatistic(unsigned i)
  1181. {
  1182. return statistics.item(curIndex + i);
  1183. }
  1184. bool initScope()
  1185. {
  1186. if (!statistics.isItem(curIndex))
  1187. return false;
  1188. unsigned next = curIndex+1;
  1189. while (next < statistics.ordinality())
  1190. {
  1191. if (compareStatisticScopes(statistics.item(curIndex), statistics.item(next)) != 0)
  1192. break;
  1193. next++;
  1194. }
  1195. numStatistics = (next - curIndex);
  1196. return true;
  1197. }
  1198. void finish()
  1199. {
  1200. curIndex = statistics.ordinality();
  1201. }
  1202. protected:
  1203. IArrayOf<IConstWUStatistic> statistics;
  1204. unsigned curIndex = 0;
  1205. unsigned numStatistics = 1;
  1206. };
  1207. /*
  1208. * An implementation of IConstWUScopeIterator for the query graphs.
  1209. */
  1210. class GraphScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
  1211. {
  1212. private:
  1213. //This uses a state machine - this enumeration contains the different states.
  1214. enum State
  1215. {
  1216. //Following are the states which represent valid scopes
  1217. SGraph,
  1218. SChildGraph,
  1219. SSubGraph,
  1220. SEdge,
  1221. SActivity,
  1222. //The following are internal states.
  1223. SGraphFirstEdge,
  1224. SGraphFirstSubGraph,
  1225. SGraphFirst,
  1226. SGraphEnd,
  1227. SGraphNext,
  1228. SSubGraphFirstEdge,
  1229. SSubGraphFirstActivity,
  1230. SSubGraphEnd,
  1231. SSubGraphNext,
  1232. SEdgeNext,
  1233. SEdgeEnd,
  1234. SActivityNext,
  1235. SActivityEnd,
  1236. SChildGraphFirstEdge,
  1237. SChildGraphFirstSubGraph,
  1238. SChildGraphFirst,
  1239. SChildGraphNext,
  1240. SChildGraphEnd,
  1241. SDone
  1242. };
  1243. State state = SDone;
  1244. State nextState = SDone;
  1245. public:
  1246. GraphScopeIterator(const IConstWorkUnit * wu, const ScopeFilter & _filter) : graphIter(&wu->getGraphs(GraphTypeAny)), filter(_filter)
  1247. {
  1248. }
  1249. virtual bool first() override
  1250. {
  1251. state = SGraphFirst;
  1252. return nextScope();
  1253. }
  1254. virtual bool next() override
  1255. {
  1256. if (!selectNext())
  1257. return false;
  1258. return nextScope();
  1259. }
  1260. virtual bool nextSibling() override
  1261. {
  1262. if (!selectNextSibling())
  1263. return false;
  1264. return nextScope();
  1265. }
  1266. virtual bool nextParent() override
  1267. {
  1268. if (!selectNextParent())
  1269. return false;
  1270. return nextScope();
  1271. }
  1272. virtual bool isValid() override
  1273. {
  1274. return graphIter->isValid();
  1275. }
  1276. virtual const char * queryScope() const override
  1277. {
  1278. return curScopeName.str();
  1279. }
  1280. virtual StatisticScopeType getScopeType() const override
  1281. {
  1282. return scopeType;
  1283. }
  1284. virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
  1285. {
  1286. switch (scopeType)
  1287. {
  1288. case SSTgraph:
  1289. return;
  1290. }
  1291. IPropertyTree & cur = treeIters.tos().query();
  1292. switch (scopeType)
  1293. {
  1294. case SSTgraph:
  1295. break;
  1296. case SSTsubgraph:
  1297. break;
  1298. case SSTactivity:
  1299. {
  1300. if (whichProperties & PTattributes)
  1301. {
  1302. playAttribute(visitor, WaLabel);
  1303. Owned<IPropertyTreeIterator> attrs = cur.getElements("att");
  1304. ForEach(*attrs)
  1305. {
  1306. IPropertyTree & cur = attrs->query();
  1307. WuAttr attr = queryGraphChildAttToWuAttr(cur.queryProp("@name"));
  1308. if (attr != WaNone)
  1309. visitor.noteAttribute(attr, cur.queryProp("@value"));
  1310. }
  1311. }
  1312. if (whichProperties & PThints)
  1313. {
  1314. Owned<IPropertyTreeIterator> hints = cur.getElements("hint");
  1315. ForEach(*hints)
  1316. {
  1317. IPropertyTree & cur = hints->query();
  1318. visitor.noteHint(cur.queryProp("@name"), cur.queryProp("@value"));
  1319. }
  1320. }
  1321. break;
  1322. }
  1323. case SSTedge:
  1324. if (whichProperties & PTattributes)
  1325. {
  1326. //MORE This will eventually need to walk the attributes and map the names.
  1327. //Need to be careful if they need to be mapped differently depending on the context.
  1328. playAttribute(visitor, WaLabel);
  1329. playAttribute(visitor, WaIdSource);
  1330. playAttribute(visitor, WaIdTarget);
  1331. playAttribute(visitor, WaSourceIndex);
  1332. playAttribute(visitor, WaTargetIndex);
  1333. playAttribute(visitor, WaIsDependency);
  1334. }
  1335. break;
  1336. }
  1337. }
  1338. virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
  1339. {
  1340. return false;
  1341. }
  1342. virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
  1343. {
  1344. if (!treeIters.ordinality())
  1345. return nullptr;
  1346. //MORE - check that the attribute is value for the current scope type (to prevent defaults being returned)
  1347. return queryAttributeValue(treeIters.tos().query(), attr, scratchpad);
  1348. }
  1349. virtual const char * queryHint(const char * kind) const override
  1350. {
  1351. //MORE: Needs to be implemented!
  1352. return nullptr;
  1353. }
  1354. private:
  1355. void playAttribute(IWuScopeVisitor & visitor, WuAttr kind)
  1356. {
  1357. StringBuffer scratchpad;
  1358. const char * value = queryAttributeValue(treeIters.tos().query(), kind, scratchpad);
  1359. if (value)
  1360. visitor.noteAttribute(kind, value);
  1361. }
  1362. void pushIterator(IPropertyTreeIterator * iter, State state)
  1363. {
  1364. treeIters.append(*LINK(iter));
  1365. stateStack.append(state);
  1366. }
  1367. State popIterator()
  1368. {
  1369. treeIters.pop();
  1370. return (State)stateStack.popGet();
  1371. }
  1372. void pushScope(const char * id)
  1373. {
  1374. scopeLengths.append(curScopeName.length());
  1375. curScopeName.append(":").append(id);
  1376. }
  1377. void popScope()
  1378. {
  1379. curScopeName.setLength(scopeLengths.popGet());
  1380. }
  1381. bool doNextScope()
  1382. {
  1383. for(;;)
  1384. {
  1385. switch (state)
  1386. {
  1387. case SGraph:
  1388. {
  1389. IConstWUGraph & graph = graphIter->query();
  1390. unsigned wfid = graph.getWfid();
  1391. curScopeName.clear();
  1392. if (wfid != 0)
  1393. curScopeName.append(WorkflowScopePrefix).append(wfid).append(':');
  1394. graph.getName(StringBufferAdaptor(curScopeName));
  1395. scopeType = SSTgraph;
  1396. return true;
  1397. }
  1398. case SSubGraph:
  1399. scopeId.set(SubGraphScopePrefix).append(treeIters.tos().query().getPropInt("@id"));
  1400. pushScope(scopeId);
  1401. scopeType = SSTsubgraph;
  1402. return true;
  1403. case SEdge:
  1404. scopeId.set(EdgeScopePrefix).append(treeIters.tos().query().queryProp("@id"));
  1405. pushScope(scopeId);
  1406. scopeType = SSTedge;
  1407. return true;
  1408. case SActivity:
  1409. if (treeIters.tos().query().getPropInt("att[@name='_kind']/@value") == TAKsubgraph)
  1410. {
  1411. state = SActivityNext;
  1412. break;
  1413. }
  1414. scopeId.set(ActivityScopePrefix).append(treeIters.tos().query().getPropInt("@id"));
  1415. pushScope(scopeId);
  1416. scopeType = SSTactivity;
  1417. return true;
  1418. case SChildGraph:
  1419. {
  1420. #ifdef _DEBUG
  1421. assertex(treeIters.tos().query().getPropInt("att[@name='_kind']/@value") == TAKsubgraph);
  1422. unsigned numIters = treeIters.ordinality();
  1423. unsigned parentActivityId = treeIters.item(numIters-2).query().getPropInt("@id");
  1424. unsigned parentId = treeIters.tos().query().getPropInt("att[@name='_parentActivity']/@value");
  1425. assertex(parentId == parentActivityId);
  1426. #endif
  1427. scopeId.set(ChildGraphScopePrefix).append(treeIters.tos().query().getPropInt("@id"));
  1428. pushScope(scopeId);
  1429. scopeType = SSTchildgraph;
  1430. return true;
  1431. }
  1432. //Graph iteration
  1433. case SGraphFirst:
  1434. if (!graphIter->first())
  1435. state = SDone;
  1436. else
  1437. state = SGraph;
  1438. break;
  1439. case SGraphEnd:
  1440. state = SGraphNext;
  1441. break;
  1442. case SGraphNext:
  1443. if (!graphIter->next())
  1444. state = SDone;
  1445. else
  1446. state = SGraph;
  1447. break;
  1448. //Edge iteration
  1449. case SGraphFirstEdge:
  1450. {
  1451. //Walk dependencies - should possibly have a different SST e.g., SSTdependency since they do not
  1452. //share many characteristics with edges - e.g. no flowing records => few/no stats.
  1453. curGraph.setown(graphIter->query().getXGMMLTree(false, false));
  1454. Owned<IPropertyTreeIterator> treeIter = curGraph->getElements("edge");
  1455. if (treeIter && treeIter->first())
  1456. {
  1457. treeIter.setown(createSortedIterator(*treeIter, compareEdgeNode));
  1458. treeIter->first();
  1459. pushIterator(treeIter, SGraphFirstSubGraph);
  1460. state = SEdge;
  1461. }
  1462. else
  1463. state = SGraphFirstSubGraph;
  1464. break;
  1465. }
  1466. case SChildGraphFirstEdge:
  1467. {
  1468. Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/edge");
  1469. if (treeIter && treeIter->first())
  1470. {
  1471. treeIter.setown(createSortedIterator(*treeIter, compareEdgeNode));
  1472. pushIterator(treeIter, SChildGraphFirstSubGraph);
  1473. state = SEdge;
  1474. }
  1475. else
  1476. state = SChildGraphFirstSubGraph;
  1477. break;
  1478. }
  1479. case SEdgeEnd:
  1480. popScope();
  1481. state = SEdgeNext;
  1482. break;
  1483. case SEdgeNext:
  1484. if (treeIters.tos().next())
  1485. state = SEdge;
  1486. else
  1487. state = popIterator();
  1488. break;
  1489. //Subgraph iteration
  1490. case SGraphFirstSubGraph:
  1491. {
  1492. Owned<IPropertyTreeIterator> treeIter = curGraph->getElements("node");
  1493. if (treeIter && treeIter->first())
  1494. {
  1495. treeIter.setown(createSortedIterator(*treeIter, compareSubGraphNode));
  1496. treeIter->first();
  1497. pushIterator(treeIter, SGraphNext);
  1498. state = SSubGraph;
  1499. }
  1500. else
  1501. state = SGraphNext;
  1502. break;
  1503. }
  1504. case SChildGraphFirstSubGraph:
  1505. {
  1506. Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/node");
  1507. if (treeIter && treeIter->first())
  1508. {
  1509. treeIter.setown(createSortedIterator(*treeIter, compareSubGraphNode));
  1510. pushIterator(treeIter, SChildGraphEnd);
  1511. state = SSubGraph;
  1512. }
  1513. else
  1514. state = SChildGraphEnd;
  1515. break;
  1516. }
  1517. case SSubGraphFirstEdge:
  1518. {
  1519. Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/edge");
  1520. if (treeIter && treeIter->first())
  1521. {
  1522. treeIter.setown(createSortedIterator(*treeIter, compareEdgeNode));
  1523. pushIterator(treeIter, SSubGraphFirstActivity);
  1524. state = SEdge;
  1525. }
  1526. else
  1527. state = SSubGraphFirstActivity;
  1528. break;
  1529. }
  1530. case SSubGraphFirstActivity:
  1531. {
  1532. //Walk the contents of each subgraph once, splitting the entries into activities and child graphs
  1533. IArrayOf<IPropertyTree> activities;
  1534. IArrayOf<IPropertyTree> childGraphs;
  1535. Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/node");
  1536. ForEach(*treeIter)
  1537. {
  1538. IPropertyTree & cur = treeIter->get();
  1539. if (cur.getPropInt("att[@name='_kind']/@value") != TAKsubgraph)
  1540. activities.append(cur);
  1541. else
  1542. childGraphs.append(cur);
  1543. }
  1544. if (activities.ordinality())
  1545. {
  1546. //Create an iterator for the child graphs in this subgraph which is iterated in order as the activities are processed
  1547. Owned<IPropertyTreeIterator> graphIter = createSortedIterator(childGraphs, compareSubGraphNode);
  1548. graphIter->first();
  1549. childGraphIters.append(*graphIter.getClear());
  1550. Owned<IPropertyTreeIterator> activityIter = createSortedIterator(activities, compareActivityNode);
  1551. pushIterator(activityIter, SSubGraphEnd);
  1552. state = SActivity;
  1553. }
  1554. else
  1555. state = SSubGraphEnd;
  1556. break;
  1557. }
  1558. case SSubGraphEnd:
  1559. popScope();
  1560. state = SSubGraphNext;
  1561. break;
  1562. case SChildGraphEnd:
  1563. popScope();
  1564. state = SChildGraphNext;
  1565. break;
  1566. case SSubGraphNext:
  1567. if (treeIters.tos().next())
  1568. state = SSubGraph;
  1569. else
  1570. state = popIterator();
  1571. break;
  1572. case SActivityEnd:
  1573. popScope();
  1574. state = SActivityNext;
  1575. break;
  1576. case SActivityNext:
  1577. if (treeIters.tos().next())
  1578. state = SActivity;
  1579. else
  1580. {
  1581. assertex(!childGraphIters.tos().isValid());
  1582. childGraphIters.pop();
  1583. state = popIterator();
  1584. }
  1585. break;
  1586. case SChildGraphNext:
  1587. if (treeIters.tos().next())
  1588. state = SChildGraph;
  1589. else
  1590. state = popIterator();
  1591. break;
  1592. case SChildGraphFirst:
  1593. {
  1594. unsigned parentActivityId = treeIters.tos().query().getPropInt("@id");
  1595. IArrayOf<IPropertyTree> childGraphs;
  1596. IPropertyTreeIterator & allGraphs = childGraphIters.tos();
  1597. while (allGraphs.isValid())
  1598. {
  1599. IPropertyTree & cur = allGraphs.query();
  1600. unsigned parentId = cur.getPropInt("att[@name='_parentActivity']/@value");
  1601. if (parentId != parentActivityId)
  1602. break;
  1603. childGraphs.append(OLINK(cur));
  1604. allGraphs.next();
  1605. }
  1606. if (childGraphs.ordinality())
  1607. {
  1608. Owned<IPropertyTreeIterator> treeIter = createSortedIterator(childGraphs, compareSubGraphNode);
  1609. pushIterator(treeIter, SActivityEnd);
  1610. state = SChildGraph;
  1611. }
  1612. else
  1613. state = SActivityEnd;
  1614. break;
  1615. }
  1616. case SDone:
  1617. return false;
  1618. default:
  1619. throwUnexpected();
  1620. }
  1621. }
  1622. }
  1623. bool nextScope()
  1624. {
  1625. for(;;)
  1626. {
  1627. if (!doNextScope())
  1628. return false;
  1629. ScopeCompare cmp = filter.compare(curScopeName);
  1630. if (cmp & SCequal)
  1631. return true;
  1632. //MORE: Optimize next based on result of compare
  1633. if (!selectNext())
  1634. return false;
  1635. }
  1636. }
  1637. bool selectNext()
  1638. {
  1639. switch (state)
  1640. {
  1641. case SGraph:
  1642. state = SGraphFirstEdge;
  1643. break;
  1644. case SChildGraph:
  1645. state = SChildGraphFirstEdge;
  1646. break;
  1647. case SSubGraph:
  1648. state = SSubGraphFirstEdge;
  1649. break;
  1650. case SEdge:
  1651. state = SEdgeEnd;
  1652. break;
  1653. case SActivity:
  1654. state = SChildGraphFirst;
  1655. break;
  1656. case SDone:
  1657. return false;
  1658. default:
  1659. throwUnexpected();
  1660. }
  1661. return true;
  1662. }
  1663. bool selectNextSibling()
  1664. {
  1665. switch (state)
  1666. {
  1667. case SGraph:
  1668. state = SGraphEnd;
  1669. break;
  1670. case SChildGraph:
  1671. state = SChildGraphEnd;
  1672. break;
  1673. case SSubGraph:
  1674. state = SSubGraphEnd;
  1675. break;
  1676. case SEdge:
  1677. state = SEdgeEnd;
  1678. break;
  1679. case SActivity:
  1680. state = SActivityEnd;
  1681. break;
  1682. case SDone:
  1683. return false;
  1684. default:
  1685. throwUnexpected();
  1686. }
  1687. return true;
  1688. }
  1689. bool selectNextParent()
  1690. {
  1691. switch (state)
  1692. {
  1693. case SGraph:
  1694. state = SDone;
  1695. break;
  1696. case SActivity:
  1697. childGraphIters.pop();
  1698. //fall through
  1699. case SChildGraph:
  1700. case SSubGraph:
  1701. case SEdge:
  1702. popScope();
  1703. state = popIterator();
  1704. break;
  1705. case SDone:
  1706. return false;
  1707. default:
  1708. throwUnexpected();
  1709. }
  1710. return true;
  1711. }
  1712. protected:
  1713. const ScopeFilter & filter;
  1714. Owned<IConstWUGraphIterator> graphIter;
  1715. Owned<IPropertyTree> curGraph;
  1716. IArrayOf<IPropertyTreeIterator> treeIters;
  1717. IArrayOf<IPropertyTreeIterator> childGraphIters;
  1718. UnsignedArray scopeLengths;
  1719. UnsignedArray stateStack;
  1720. StringBuffer curScopeName;
  1721. StringBuffer scopeId;
  1722. StatisticScopeType scopeType = SSTnone;
  1723. };
  1724. static int compareWorkflow(IInterface * const * pLeft, IInterface * const * pRight)
  1725. {
  1726. IConstWorkflowItem * left = static_cast<IConstWorkflowItem *>(*pLeft);
  1727. IConstWorkflowItem * right = static_cast<IConstWorkflowItem *>(*pRight);
  1728. return left->queryWfid() - right->queryWfid();
  1729. }
  1730. static const char * trueToStr(bool value) { return value ? "true" : nullptr; }
  1731. /*
  1732. * An implementation of IConstWUScopeIterator for the workflow information.
  1733. */
  1734. class WorkflowStatisticsScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
  1735. {
  1736. public:
  1737. WorkflowStatisticsScopeIterator(IConstWorkflowItemIterator * wfIter)
  1738. {
  1739. ForEach(*wfIter)
  1740. workflow.append(*LINK(wfIter->query()));
  1741. workflow.sort(compareWorkflow);
  1742. }
  1743. virtual bool first() override
  1744. {
  1745. if (workflow.empty())
  1746. return false;
  1747. curWorkflow = 0;
  1748. return initWorkflowItem();
  1749. }
  1750. virtual bool next() override
  1751. {
  1752. if (!workflow.isItem(++curWorkflow))
  1753. return false;
  1754. return initWorkflowItem();
  1755. }
  1756. virtual bool nextSibling() override
  1757. {
  1758. return next();
  1759. }
  1760. virtual bool nextParent() override
  1761. {
  1762. curWorkflow = NotFound;
  1763. return false;
  1764. }
  1765. virtual bool isValid() override
  1766. {
  1767. return workflow.isItem(curWorkflow);
  1768. }
  1769. virtual const char * queryScope() const override
  1770. {
  1771. return curScope.str();
  1772. }
  1773. virtual StatisticScopeType getScopeType() const override
  1774. {
  1775. return SSTworkflow;
  1776. }
  1777. virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
  1778. {
  1779. if (whichProperties & PTattributes)
  1780. {
  1781. {
  1782. StringBuffer scratchpad;
  1783. Owned<IWorkflowDependencyIterator> depends = workflow.item(curWorkflow).getDependencies();
  1784. ForEach(*depends)
  1785. visitor.noteAttribute(WaIdDependency, getValueText(depends->query(), scratchpad, WorkflowScopePrefix));
  1786. }
  1787. play(visitor, { WaIsScheduled, WaIdSuccess, WaIdFailure, WaIdRecovery, WaIdPersist, WaIdScheduled,
  1788. WaPersistName, WaLabel, WaMode, WaType, WaState, WaCluster, WaCriticalSection });
  1789. }
  1790. }
  1791. virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
  1792. {
  1793. return false;
  1794. }
  1795. virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
  1796. {
  1797. auto wf = &workflow.item(curWorkflow);
  1798. StringBufferAdaptor adaptor(scratchpad);
  1799. switch (attr)
  1800. {
  1801. case WaIdDependencyList:
  1802. {
  1803. bool first = true;
  1804. Owned<IWorkflowDependencyIterator> depends = workflow.item(curWorkflow).getDependencies();
  1805. ForEach(*depends)
  1806. {
  1807. if (first)
  1808. scratchpad.append("[");
  1809. else
  1810. scratchpad.append(",");
  1811. scratchpad.append('"').append(WorkflowScopePrefix).append(depends->query()).append('"');
  1812. first = false;
  1813. }
  1814. if (first)
  1815. return nullptr;
  1816. scratchpad.append("]");
  1817. return scratchpad.str();
  1818. }
  1819. case WaIsScheduled: return trueToStr(wf->isScheduled());
  1820. case WaIdSuccess: return getWfidText(wf->querySuccess(), scratchpad);
  1821. case WaIdFailure: return getWfidText(wf->queryFailure(), scratchpad);
  1822. case WaIdRecovery: return getWfidText(wf->queryRecovery(), scratchpad);
  1823. case WaIdPersist: return getWfidText(wf->queryPersistWfid(), scratchpad);
  1824. case WaIdScheduled: return getWfidText(wf->queryScheduledWfid(), scratchpad);
  1825. case WaPersistName: return queryOptString(wf->getPersistName(adaptor));
  1826. case WaLabel: return queryOptString(wf->getLabel(adaptor));
  1827. case WaMode: return wf->queryMode() != WFModeNormal ? queryWorkflowModeText(wf->queryMode()) : nullptr;
  1828. case WaType: return wf->queryType() != WFTypeNormal ? queryWorkflowTypeText(wf->queryType()) : nullptr;
  1829. case WaState: return queryWorkflowStateText(wf->queryState());
  1830. case WaCluster: return queryOptString(wf->queryCluster(adaptor));
  1831. case WaCriticalSection: return queryOptString(wf->getCriticalName(adaptor));
  1832. /*
  1833. The followng attributes are not generated - I'm not convinced they are very useful, but they could be added later.
  1834. virtual bool isScheduledNow() const = 0;
  1835. virtual IWorkflowEvent * getScheduleEvent() const = 0;
  1836. virtual unsigned querySchedulePriority() const = 0;
  1837. virtual bool hasScheduleCount() const = 0;
  1838. virtual unsigned queryScheduleCount() const = 0;
  1839. virtual unsigned queryRetriesAllowed() const = 0;
  1840. virtual int queryPersistCopies() const = 0; // 0 - unmangled name, < 0 - use default, > 0 - max number
  1841. virtual bool queryPersistRefresh() const = 0;
  1842. virtual unsigned queryScheduleCountRemaining() const = 0;
  1843. virtual unsigned queryRetriesRemaining() const = 0;
  1844. virtual int queryFailCode() const = 0;
  1845. virtual const char * queryFailMessage() const = 0;
  1846. virtual const char * queryEventName() const = 0;
  1847. virtual const char * queryEventExtra() const = 0;
  1848. */
  1849. }
  1850. return nullptr;
  1851. }
  1852. virtual const char * queryHint(const char * kind) const
  1853. {
  1854. return nullptr;
  1855. }
  1856. protected:
  1857. bool initWorkflowItem()
  1858. {
  1859. curScope.clear().append(WorkflowScopePrefix).append(workflow.item(curWorkflow).queryWfid());
  1860. return true;
  1861. }
  1862. const char * getValueText(unsigned value, StringBuffer & scratchpad, const char * prefix = nullptr) const
  1863. {
  1864. return scratchpad.clear().append(prefix).append(value).str();
  1865. }
  1866. const char * queryOptString(IStringVal & value) const
  1867. {
  1868. const char * text = value.str();
  1869. if (!text || !*text)
  1870. return nullptr;
  1871. return text;
  1872. }
  1873. const char * getWfidText(unsigned value, StringBuffer & scratchpad) const
  1874. {
  1875. if (!value)
  1876. return nullptr;
  1877. return scratchpad.clear().append(WorkflowScopePrefix).append(value).str();
  1878. }
  1879. void play(IWuScopeVisitor & visitor, const std::initializer_list<WuAttr> & attrs) const
  1880. {
  1881. StringBuffer scratchpad;
  1882. for (auto attr : attrs)
  1883. {
  1884. const char * value = queryAttribute(attr, scratchpad.clear());
  1885. if (value)
  1886. visitor.noteAttribute(attr, value);
  1887. }
  1888. }
  1889. protected:
  1890. IArrayOf<IConstWorkflowItem> workflow;
  1891. unsigned curWorkflow = 0;
  1892. StringBuffer curScope;
  1893. };
  1894. /*
  1895. * An implementation of IConstWUScopeIterator that combines results from multiple sources.
  1896. */
  1897. class CompoundStatisticsScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
  1898. {
  1899. class VisitorMapper : implements IWuScopeVisitor
  1900. {
  1901. public:
  1902. VisitorMapper(const WuScopeFilter & _filter, IWuScopeVisitor & _visitor) :
  1903. filter(_filter), visitor(_visitor)
  1904. {}
  1905. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
  1906. {
  1907. if (filter.includeStatistic(kind))
  1908. visitor.noteStatistic(kind, value, extra);
  1909. }
  1910. virtual void noteAttribute(WuAttr attr, const char * value) override
  1911. {
  1912. if (filter.includeAttribute(attr))
  1913. visitor.noteAttribute(attr, value);
  1914. }
  1915. virtual void noteHint(const char * kind, const char * value) override
  1916. {
  1917. if (filter.includeHint(kind))
  1918. visitor.noteHint(kind, value);
  1919. }
  1920. virtual void noteException(IConstWUException & exception) override
  1921. {
  1922. visitor.noteException(exception);
  1923. }
  1924. protected:
  1925. const WuScopeFilter & filter;
  1926. IWuScopeVisitor & visitor;
  1927. };
  1928. public:
  1929. CompoundStatisticsScopeIterator(const WuScopeFilter & _filter) : filter(_filter)
  1930. {
  1931. }
  1932. void addIter(IConstWUScopeIterator * iter)
  1933. {
  1934. if (iter)
  1935. {
  1936. iters.append(OLINK(*iter));
  1937. assertex(iters.ordinality() <= sizeof(activeIterMask)*8);
  1938. }
  1939. }
  1940. virtual bool first() override
  1941. {
  1942. activeIterMask = 0;
  1943. ForEachItemIn(i, iters)
  1944. {
  1945. if (iters.item(i).first())
  1946. activeIterMask |= (1U << i);
  1947. }
  1948. return findNextScope();
  1949. }
  1950. virtual bool next() override
  1951. {
  1952. selectNext();
  1953. return findNextScope();
  1954. }
  1955. virtual bool nextSibling() override
  1956. {
  1957. selectNextSibling();
  1958. return findNextScope();
  1959. }
  1960. virtual bool nextParent() override
  1961. {
  1962. selectNextParent();
  1963. return findNextScope();
  1964. }
  1965. virtual bool isValid() override
  1966. {
  1967. return (activeIterMask != 0);
  1968. }
  1969. virtual const char * queryScope() const override
  1970. {
  1971. return iters.item(firstMatchIter).queryScope();
  1972. }
  1973. virtual StatisticScopeType getScopeType() const override
  1974. {
  1975. return iters.item(firstMatchIter).getScopeType();
  1976. }
  1977. virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
  1978. {
  1979. VisitorMapper mappedVisitor(filter, visitor);
  1980. whichProperties &= filter.properties;
  1981. ForEachItemIn(i, iters)
  1982. {
  1983. if (iterMatchesCurrentScope(i))
  1984. iters.item(i).playProperties(mappedVisitor, whichProperties);
  1985. }
  1986. }
  1987. virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
  1988. {
  1989. ForEachItemIn(i, iters)
  1990. {
  1991. if (iterMatchesCurrentScope(i))
  1992. {
  1993. if (iters.item(i).getStat(kind, value))
  1994. return true;
  1995. }
  1996. }
  1997. return false;
  1998. }
  1999. virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
  2000. {
  2001. ForEachItemIn(i, iters)
  2002. {
  2003. if (iterMatchesCurrentScope(i))
  2004. {
  2005. const char * value = iters.item(i).queryAttribute(attr, scratchpad);
  2006. if (value)
  2007. return value;
  2008. }
  2009. }
  2010. return nullptr;
  2011. }
  2012. virtual const char * queryHint(const char * kind) const override
  2013. {
  2014. ForEachItemIn(i, iters)
  2015. {
  2016. if (iterMatchesCurrentScope(i))
  2017. {
  2018. const char * value = iters.item(i).queryHint(kind);
  2019. if (value)
  2020. return value;
  2021. }
  2022. }
  2023. return nullptr;
  2024. }
  2025. inline bool iterMatchesCurrentScope(unsigned i) const { return ((1U << i) & matchIterMask) != 0; }
  2026. inline bool isAlive(unsigned i) const { return ((1U << i) & activeIterMask) != 0; }
  2027. protected:
  2028. //Calculate which iterators contain the scope that should come next
  2029. bool findNextScope()
  2030. {
  2031. for(;;)
  2032. {
  2033. if (activeIterMask == 0)
  2034. return false;
  2035. unsigned mask = 0;
  2036. const char * scope = nullptr;
  2037. ForEachItemIn(i, iters)
  2038. {
  2039. if (isAlive(i))
  2040. {
  2041. const char * iterScope = iters.item(i).queryScope();
  2042. if (mask)
  2043. {
  2044. int compare = compareScopeName(scope, iterScope);
  2045. if (compare == 0)
  2046. {
  2047. mask |= (1U << i);
  2048. }
  2049. else if (compare > 0)
  2050. {
  2051. scope = iterScope;
  2052. mask = (1U << i);
  2053. firstMatchIter = i;
  2054. }
  2055. }
  2056. else
  2057. {
  2058. scope = iterScope;
  2059. mask = (1U << i);
  2060. firstMatchIter = i;
  2061. }
  2062. }
  2063. }
  2064. matchIterMask = mask;
  2065. while (activeScopes)
  2066. {
  2067. const char * activeScope = activeScopes.tos();
  2068. //If the next scope if not a child of one of the active scopes then that active scope will no longer match.
  2069. if (compareScopes(scope, activeScope) & SCchild)
  2070. break;
  2071. activeScopes.pop();
  2072. }
  2073. //The top most scope will be the deepest. Check if the current scope is close enough to return as a match.
  2074. bool include = false;
  2075. if (activeScopes)
  2076. {
  2077. const char * activeScope = activeScopes.tos();
  2078. //code above has ensured that this scope must be a child of (and therefore deeper) than activeScope
  2079. unsigned nesting = queryScopeDepth(scope) - queryScopeDepth(activeScope);
  2080. if (nesting <= filter.include.nestedDepth)
  2081. include = true;
  2082. }
  2083. //Check to see if this is a new match
  2084. if (filter.compareMatchScopes(scope) & SCequal)
  2085. {
  2086. if (!include)
  2087. include = filter.include.matchedScope;
  2088. //Only add it to the list of active scopes if it can match child elements
  2089. if (filter.include.nestedDepth != 0)
  2090. activeScopes.append(scope);
  2091. }
  2092. if (include && !filter.includeScope(scope))
  2093. include = false;
  2094. if (include && filter.requiredStats.size())
  2095. {
  2096. //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
  2097. for (unsigned iReq=0; iReq < filter.requiredStats.size(); iReq++)
  2098. {
  2099. const StatisticValueFilter & cur = filter.requiredStats[iReq];
  2100. unsigned __int64 value;
  2101. if (getStat(cur.queryKind(), value))
  2102. {
  2103. if (!cur.matches(value))
  2104. include = false;
  2105. }
  2106. else
  2107. include = false;
  2108. }
  2109. }
  2110. if (include && filter.requiredAttrs.size())
  2111. {
  2112. //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
  2113. StringBuffer temp;
  2114. for (unsigned iReq=0; iReq < filter.requiredAttrs.size(); iReq++)
  2115. {
  2116. const AttributeValueFilter & cur = filter.requiredAttrs[iReq];
  2117. const char * value = queryAttribute(cur.queryKind(), temp.clear());
  2118. if (!value || !cur.matches(value))
  2119. {
  2120. include = false;
  2121. break;
  2122. }
  2123. }
  2124. }
  2125. if (include)
  2126. return true;
  2127. //MORE: Optimize based on filter.compareScope()
  2128. selectNext();
  2129. }
  2130. }
  2131. void checkScopeOrder(unsigned input, const char * prevScope)
  2132. {
  2133. if (isAlive(input))
  2134. {
  2135. const char * curScope = iters.item(input).queryScope();
  2136. int compare = compareScopeName(prevScope, curScope);
  2137. if (compare >= 0)
  2138. throw MakeStringException(0, "Out of order (%u) scopes %s,%s = %d", input, prevScope, curScope, compare);
  2139. }
  2140. }
  2141. void selectNext()
  2142. {
  2143. ForEachItemIn(i, iters)
  2144. {
  2145. if (iterMatchesCurrentScope(i))
  2146. {
  2147. #ifdef _DEBUG
  2148. StringBuffer prevScope(iters.item(i).queryScope());
  2149. #endif
  2150. if (!iters.item(i).next())
  2151. activeIterMask &= ~(1U << i);
  2152. #ifdef _DEBUG
  2153. checkScopeOrder(i, prevScope);
  2154. #endif
  2155. }
  2156. }
  2157. }
  2158. void selectNextSibling()
  2159. {
  2160. ForEachItemIn(i, iters)
  2161. {
  2162. if (iterMatchesCurrentScope(i))
  2163. {
  2164. #ifdef _DEBUG
  2165. StringBuffer prevScope(iters.item(i).queryScope());
  2166. #endif
  2167. if (!iters.item(i).nextSibling())
  2168. activeIterMask &= ~(1U << i);
  2169. #ifdef _DEBUG
  2170. checkScopeOrder(i, prevScope);
  2171. #endif
  2172. }
  2173. }
  2174. }
  2175. void selectNextParent()
  2176. {
  2177. ForEachItemIn(i, iters)
  2178. {
  2179. if (iterMatchesCurrentScope(i))
  2180. {
  2181. #ifdef _DEBUG
  2182. StringBuffer prevScope(iters.item(i).queryScope());
  2183. #endif
  2184. if (!iters.item(i).nextParent())
  2185. activeIterMask &= ~(1U << i);
  2186. #ifdef _DEBUG
  2187. checkScopeOrder(i, prevScope);
  2188. #endif
  2189. }
  2190. }
  2191. }
  2192. protected:
  2193. const WuScopeFilter & filter;
  2194. IArrayOf<IConstWUScopeIterator> iters;
  2195. unsigned curIter = 0;
  2196. unsigned firstMatchIter = 0;
  2197. unsigned matchIterMask = 0; // bit set of iterators which have a valid entry for the current scope
  2198. unsigned activeIterMask = 0; // bit set of iterators which are still active
  2199. StringArray activeScopes;
  2200. };
  2201. //---------------------------------------------------------------------------------------------------------------------
  2202. class AggregateFilter
  2203. {
  2204. public:
  2205. AggregateFilter(StatisticKind _search) : search(_search) {}
  2206. inline bool matches(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) const
  2207. {
  2208. return (kind == search);
  2209. }
  2210. protected:
  2211. StatisticKind search;
  2212. //MORE: Allow filtering by scope?
  2213. };
  2214. class StatisticAggregator : public CInterfaceOf<IWuScopeVisitor>
  2215. {
  2216. public:
  2217. StatisticAggregator(StatisticKind _search) : filter(_search) {}
  2218. virtual void noteAttribute(WuAttr attr, const char * value) override { throwUnexpected(); }
  2219. virtual void noteHint(const char * kind, const char * value) override { throwUnexpected(); }
  2220. virtual void noteException(IConstWUException & exception) { throwUnexpected();}
  2221. protected:
  2222. AggregateFilter filter;
  2223. };
  2224. class SimpleAggregator : public StatisticAggregator
  2225. {
  2226. public:
  2227. SimpleAggregator(StatisticKind _search) : StatisticAggregator(_search) {}
  2228. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
  2229. {
  2230. if (filter.matches(kind, value, extra))
  2231. summary.noteValue(value);
  2232. }
  2233. //How should these be reported? Should there be a playAggregates(IWuAggregatedScopeVisitor)
  2234. //with a noteAggregate(value, variant, value, grouping)?
  2235. protected:
  2236. StatsAggregation summary;
  2237. };
  2238. class SimpleReferenceAggregator : public StatisticAggregator
  2239. {
  2240. public:
  2241. SimpleReferenceAggregator(StatisticKind _search, StatsAggregation & _summary) : StatisticAggregator(_search), summary(_summary) {}
  2242. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
  2243. {
  2244. if (filter.matches(kind, value, extra))
  2245. summary.noteValue(value);
  2246. }
  2247. //How should these be reported? Should there be a playAggregates(IWuAggregatedScopeVisitor)
  2248. //with a noteAggregate(value, variant, value, grouping)?
  2249. protected:
  2250. StatsAggregation & summary;
  2251. };
  2252. class GroupedAggregator : public StatisticAggregator
  2253. {
  2254. public:
  2255. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
  2256. {
  2257. if (filter.matches(kind, value, extra))
  2258. {
  2259. StatsAggregation & match = summary; // look up in hash table.
  2260. match.noteValue(value);
  2261. }
  2262. }
  2263. protected:
  2264. //HashTable of (possible groupings->summary)
  2265. StatsAggregation summary;
  2266. };
  2267. class CompoundAggregator : implements StatisticAggregator
  2268. {
  2269. public:
  2270. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra)
  2271. {
  2272. ForEachItemIn(i, aggregators)
  2273. aggregators.item(i).noteStatistic(kind, value, extra);
  2274. }
  2275. protected:
  2276. IArrayOf<StatisticAggregator> aggregators;
  2277. };
  2278. // Aggregate costs excluding all hThor costs
  2279. // Note: the only reason that this class is required is that it is not possible to determine the creator
  2280. // of a scope when iterating with IConstWUScopeIterator. (When this functionlity becomes available,
  2281. // consider filtering within aggregateCost and eliminating this class.)
  2282. class CostAggregatorExcludeHThor : public CInterfaceOf<IWuScopeVisitor>
  2283. {
  2284. public:
  2285. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
  2286. {
  2287. if (extra.getCreatorType() != SCThthor)
  2288. totalCost += value;
  2289. }
  2290. virtual void noteAttribute(WuAttr attr, const char * value) override { throwUnexpected(); }
  2291. virtual void noteHint(const char * kind, const char * value) override { throwUnexpected(); }
  2292. virtual void noteException(IConstWUException & exception) { throwUnexpected(); }
  2293. virtual cost_type getTotalCost() const { return totalCost; }
  2294. protected:
  2295. cost_type totalCost = 0;
  2296. };
  2297. // Aggregrate Thor or hThor costs
  2298. cost_type aggregateCost(const IConstWorkUnit * wu, const char *scope, bool excludeHThor)
  2299. {
  2300. // depth 1: workflow, depth 2: graph, depth 3: subgraph
  2301. WuScopeFilter filter;
  2302. if (scope && *scope) // All costs under specified scope (used to calculate all costs for a workflow)
  2303. {
  2304. filter.addScope(scope);
  2305. filter.setIncludeNesting(3);
  2306. filter.addOutputStatistic(StCostExecute);
  2307. filter.addRequiredStat(StCostExecute);
  2308. }
  2309. else
  2310. filter.addFilter("stat[CostExecute],depth[1..3],nested[0],where[CostExecute]");
  2311. filter.addSource("global");
  2312. filter.finishedFilter();
  2313. Owned<IConstWUScopeIterator> it = &wu->getScopeIterator(filter);
  2314. // Note: use of CostAggregatorExcludeHThor will be a slower, so use only when necessary
  2315. if (excludeHThor)
  2316. {
  2317. CostAggregatorExcludeHThor aggregator;
  2318. for (it->first(); it->isValid(); )
  2319. {
  2320. it->playProperties(aggregator);
  2321. stat_type value;
  2322. if (it->getStat(StCostExecute, value))
  2323. it->nextSibling();
  2324. else
  2325. it->next();
  2326. }
  2327. return aggregator.getTotalCost();
  2328. }
  2329. else
  2330. {
  2331. cost_type totalCost = 0;
  2332. for (it->first(); it->isValid(); )
  2333. {
  2334. stat_type value = 0;
  2335. if (it->getStat(StCostExecute, value))
  2336. {
  2337. totalCost += value;
  2338. it->nextSibling();
  2339. }
  2340. else
  2341. it->next();
  2342. }
  2343. return totalCost;
  2344. }
  2345. }
  2346. //aggregate disk costs from top-level subgraphs (when scope specified) or workflows (scope not specified)
  2347. cost_type aggregateDiskAccessCost(const IConstWorkUnit * wu, const char *scope)
  2348. {
  2349. WuScopeFilter filter;
  2350. if (!isEmptyString(scope))
  2351. filter.addScope(scope);
  2352. else
  2353. filter.addScope(""); // Needed to match scope
  2354. // when scope is a workflow, sum graph costs (or subgraph cost when no graph cost) to get workflow cost
  2355. // (Costs from child graphs and activities should have been summed up to graph/subgraph level already)
  2356. // when isEmptyString(scope), sum workflow costs (or graph cost when no workflow cost) to get global cost
  2357. // (Costs from all levels below graph should be summed upto at least graph level already)
  2358. // i.e. need 2 levels of nesting
  2359. filter.setIncludeNesting(2);
  2360. // includeNesting(2) needs just source "global". However, WuScopeFilter is incorrectly inferring the source as "global,stats",
  2361. // causing too many of the stats to be pulled in and inefficiency. Here, explicitly set source to "global"
  2362. filter.addSource("global");
  2363. filter.addOutputStatistic(StCostFileAccess);
  2364. filter.addRequiredStat(StCostFileAccess);
  2365. filter.finishedFilter();
  2366. Owned<IConstWUScopeIterator> it = &wu->getScopeIterator(filter);
  2367. cost_type totalCost = 0;
  2368. for (it->first(); it->isValid(); )
  2369. {
  2370. cost_type value = 0;
  2371. if (it->getStat(StCostFileAccess, value))
  2372. {
  2373. totalCost += value;
  2374. it->nextSibling();
  2375. }
  2376. else
  2377. {
  2378. it->next();
  2379. }
  2380. }
  2381. return totalCost;
  2382. }
  2383. //---------------------------------------------------------------------------------------------------------------------
  2384. //Extract an argument of the format abc[def[ghi...,]],... as (abc,def[...])
  2385. static bool extractOption(const char * & finger, StringBuffer & option, StringBuffer & arg)
  2386. {
  2387. const char * start = finger;
  2388. if (!*start)
  2389. return false;
  2390. const char * cur = start;
  2391. const char * bra = nullptr;
  2392. const char * end = nullptr;
  2393. unsigned braDepth = 0;
  2394. char next;
  2395. arg.clear();
  2396. for (;;)
  2397. {
  2398. next = *cur;
  2399. if (!next || ((next == ',') && (braDepth == 0)))
  2400. break;
  2401. switch (next)
  2402. {
  2403. case '[':
  2404. if (braDepth == 0)
  2405. {
  2406. if (bra)
  2407. throw makeStringExceptionV(0, "Multiple [ in filter : %s", bra);
  2408. bra = cur;
  2409. }
  2410. braDepth++;
  2411. break;
  2412. case ']':
  2413. if (braDepth > 0)
  2414. {
  2415. if (--braDepth == 0)
  2416. {
  2417. end = cur;
  2418. arg.append(cur - (bra+1), bra+1);
  2419. }
  2420. }
  2421. break;
  2422. }
  2423. cur++;
  2424. }
  2425. if (braDepth != 0)
  2426. throw makeStringExceptionV(0, "Mismatched ] in filter : %s", start);
  2427. option.clear();
  2428. if (bra)
  2429. {
  2430. if (cur != end+1)
  2431. throw makeStringExceptionV(0, "Text follows closing bracket: %s", end);
  2432. option.append(bra-start, start);
  2433. }
  2434. else
  2435. option.append(cur-start, start);
  2436. if (next)
  2437. finger = cur+1;
  2438. else
  2439. finger = cur;
  2440. return true;
  2441. }
  2442. static unsigned readOptValue(const char * start, const char * end, unsigned dft, const char * type)
  2443. {
  2444. if (start == end)
  2445. return dft;
  2446. char * next;
  2447. unsigned value = (unsigned)strtoll(start, &next, 10);
  2448. if (next != end)
  2449. throw makeStringExceptionV(0, "Unexpected characters in %s option '%s'", type, next);
  2450. return value;
  2451. }
  2452. static unsigned readValue(const char * start, const char * type)
  2453. {
  2454. if (*start == '\0')
  2455. throw makeStringExceptionV(0, "Expected a value for the %s option", type);
  2456. char * next;
  2457. unsigned value = (unsigned)strtoll(start, &next, 10);
  2458. if (*next != '\0')
  2459. throw makeStringExceptionV(0, "Unexpected characters in %s option '%s'", type, next);
  2460. return value;
  2461. }
  2462. StringBuffer & AttributeValueFilter::describe(StringBuffer & out) const
  2463. {
  2464. out.append(queryWuAttributeName(attr));
  2465. if (value)
  2466. out.append("=").append(value);
  2467. return out;
  2468. }
  2469. /*
  2470. Scope service matching Syntax: * indicates
  2471. Which items are matched:
  2472. scope[<scope-id>]* | stype[<scope-type>]* | id[<id>]* - which scopes should be matched?
  2473. depth[n | low..high] - range of depths to search for a match
  2474. source[global|stats|graph|all]* - which sources to search within the workunit
  2475. where[<statistickind> | <statistickind> (=|<|<=|>|>=) value | <statistickind>=low..high] - a statistic filter
  2476. Which items are include in the results:
  2477. matched[true|false] - are the matched scopes returned?
  2478. nested[<depth>|all] - how deep within a scope should be matched (default = 0 if matched[true], all if matched[false])
  2479. includetype[<scope-type>] - which scope types should be included?
  2480. Which properties of the items are returned:
  2481. properties[statistics|hints|attributes|scope|all]
  2482. statistic[<statistic-kind>|none|all] - include statistic
  2483. attribute[<attribute-name>|none|all] - include attribute
  2484. hint[<hint-name>] - include hint
  2485. property[<statistic-kind>|<attribute-name>|<hint-name>] - include property
  2486. measure[<measure>] - all statistics with a particular measure
  2487. version[<version>] - minimum version to return
  2488. */
  2489. enum { FOscope, FOstype, FOid, FOdepth, FOsource, FOwhere, FOmatched, FOnested, FOinclude, FOproperties, FOstatistic, FOattribute, FOhint, FOproperty, FOmeasure, FOversion, FOunknown };
  2490. //Some of the following contains aliases for the same option e.g. stat and statistic
  2491. static constexpr EnumMapping filterOptions[] = {
  2492. { FOscope, "scope" }, { FOstype, "stype" }, { FOid, "id" },
  2493. { FOdepth, "depth" }, { FOsource, "source" }, { FOwhere, "where" },
  2494. { FOmatched, "matched" }, { FOnested, "nested" },
  2495. { FOinclude, "include" }, { FOinclude, "includetype" },
  2496. { FOproperties, "props" }, { FOproperties, "properties" }, // some aliases
  2497. { FOstatistic, "stat" }, { FOstatistic, "statistic" }, { FOattribute, "attr" }, { FOattribute, "attribute" }, { FOhint, "hint" },
  2498. { FOproperty, "prop" }, { FOproperty, "property" },
  2499. { FOmeasure, "measure" }, { FOversion, "version" }, { 0, nullptr} };
  2500. static constexpr EnumMapping sourceMappings[] = {
  2501. { SSFsearchGlobalStats, "global" }, { SSFsearchGraphStats, "stats" }, { SSFsearchGraphStats, "statistics" }, { SSFsearchGraph, "graph" }, { SSFsearchExceptions, "exception" }, { SSFsearchWorkflow, "workflow" },
  2502. { (int)SSFsearchAll, "all" }, { 0, nullptr } };
  2503. static constexpr EnumMapping propertyMappings[] = {
  2504. { PTstatistics, "stat" }, { PTstatistics, "statistic" }, { PTattributes, "attr" }, { PTattributes, "attribute" }, { PThints, "hint" },{ PTnotes, "note" },
  2505. { PTstatistics, "stats" }, { PTstatistics, "statistics" }, { PTattributes, "attrs" }, { PTattributes, "attributes" }, { PThints, "hints" }, { PTnotes, "notes" },
  2506. { PTnone, "none" }, { PTscope, "scope" }, { PTall, "all" }, { 0, nullptr } };
  2507. WuScopeSourceFlags querySource(const char * source) { return (WuScopeSourceFlags)getEnum(source, sourceMappings, SSFunknown); }
  2508. const char * querySourceText(WuScopeSourceFlags source) { return getEnumText(source, sourceMappings, nullptr); }
  2509. WuScopeFilter::WuScopeFilter(const char * filter)
  2510. {
  2511. addFilter(filter);
  2512. finishedFilter();
  2513. }
  2514. WuScopeFilter & WuScopeFilter::addFilter(const char * filter)
  2515. {
  2516. checkModifiable();
  2517. if (!filter)
  2518. return *this;
  2519. StringBuffer option;
  2520. StringBuffer arg;
  2521. while (extractOption(filter, option, arg))
  2522. {
  2523. switch (getEnum(option, filterOptions, FOunknown))
  2524. {
  2525. case FOscope:
  2526. addScope(arg);
  2527. break;
  2528. case FOstype:
  2529. addScopeType(arg);
  2530. break;
  2531. case FOid:
  2532. addId(arg);
  2533. break;
  2534. case FOdepth:
  2535. {
  2536. //Allow depth[n], depth[a,b] or depth[a..b]
  2537. const char * comma = strchr(arg, ',');
  2538. const char * dotdot = strstr(arg, "..");
  2539. if (comma)
  2540. {
  2541. unsigned low = readOptValue(arg, comma, 0, "depth");
  2542. if (comma[1])
  2543. {
  2544. scopeFilter.setDepth(low, readValue(comma+1, "depth"));
  2545. }
  2546. else
  2547. scopeFilter.setDepth(low, UINT_MAX);
  2548. }
  2549. else if (dotdot)
  2550. {
  2551. unsigned low = readOptValue(arg, dotdot, 0, "depth");
  2552. if (dotdot[2])
  2553. scopeFilter.setDepth(low, readValue(dotdot+2, "depth"));
  2554. else
  2555. scopeFilter.setDepth(low, UINT_MAX);
  2556. }
  2557. else
  2558. {
  2559. scopeFilter.setDepth(readValue(arg, "depth"));
  2560. }
  2561. break;
  2562. }
  2563. case FOsource:
  2564. addSource(arg);
  2565. break;
  2566. case FOwhere: // where[stat<op>value]
  2567. addRequiredStat(arg);
  2568. break;
  2569. case FOmatched:
  2570. setIncludeMatch(strToBool(arg));
  2571. break;
  2572. case FOnested:
  2573. if (strieq(arg, "all"))
  2574. setIncludeNesting(UINT_MAX);
  2575. else if (isdigit(*arg))
  2576. setIncludeNesting(atoi(arg));
  2577. else
  2578. throw makeStringExceptionV(0, "Expected a value for the nesting depth: %s", arg.str());
  2579. break;
  2580. case FOinclude:
  2581. setIncludeScopeType(arg);
  2582. break;
  2583. case FOproperties:
  2584. {
  2585. WuPropertyTypes prop = (WuPropertyTypes)getEnum(arg, propertyMappings, PTunknown);
  2586. if (prop == PTunknown)
  2587. throw makeStringExceptionV(0, "Unexpected properties '%s'", arg.str());
  2588. addOutputProperties(prop);
  2589. break;
  2590. }
  2591. case FOstatistic:
  2592. addOutputStatistic(arg);
  2593. break;
  2594. case FOattribute:
  2595. addOutputAttribute(arg);
  2596. break;
  2597. case FOhint:
  2598. addOutputHint(arg);
  2599. break;
  2600. case FOproperty:
  2601. addOutput(arg);
  2602. break;
  2603. case FOmeasure:
  2604. setMeasure(arg);
  2605. break;
  2606. case FOversion:
  2607. if (isdigit(*arg))
  2608. minVersion = atoi64(arg);
  2609. else
  2610. throw makeStringExceptionV(0, "Expected a value for the version: %s", arg.str());
  2611. break;
  2612. default:
  2613. throw makeStringExceptionV(0, "Unrecognised filter option: %s", option.str());
  2614. }
  2615. }
  2616. return *this;
  2617. }
  2618. WuScopeFilter & WuScopeFilter::addScope(const char * scope)
  2619. {
  2620. checkModifiable();
  2621. if (scope)
  2622. {
  2623. validateScope(scope);
  2624. scopeFilter.addScope(scope);
  2625. }
  2626. return *this;
  2627. }
  2628. WuScopeFilter & WuScopeFilter::addScopeType(StatisticScopeType scopeType)
  2629. {
  2630. checkModifiable();
  2631. scopeFilter.addScopeType(scopeType);
  2632. return *this;
  2633. }
  2634. WuScopeFilter & WuScopeFilter::addScopeType(const char * scopeType)
  2635. {
  2636. checkModifiable();
  2637. if (scopeType)
  2638. {
  2639. StatisticScopeType sst = queryScopeType(scopeType, SSTmax);
  2640. if (sst == SSTmax)
  2641. throw makeStringExceptionV(0, "Unrecognised scope type '%s'", scopeType);
  2642. scopeFilter.addScopeType(sst);
  2643. }
  2644. return *this;
  2645. }
  2646. WuScopeFilter & WuScopeFilter::addId(const char * id)
  2647. {
  2648. checkModifiable();
  2649. validateScopeId(id);
  2650. scopeFilter.addId(id);
  2651. return *this;
  2652. }
  2653. WuScopeFilter & WuScopeFilter::addOutput(const char * prop)
  2654. {
  2655. checkModifiable();
  2656. WuAttr attr = queryWuAttribute(prop, WaNone);
  2657. if (attr != WaNone)
  2658. {
  2659. WuAttr singleKindAttr = getSingleKindOfListAttribute(attr);
  2660. if (singleKindAttr != WaNone)
  2661. addOutputAttribute(singleKindAttr);
  2662. else
  2663. addOutputAttribute(attr);
  2664. } else if (queryStatisticKind(prop, StMax) != StMax)
  2665. addOutputStatistic(prop);
  2666. else
  2667. addOutputHint(prop);
  2668. return *this;
  2669. }
  2670. WuScopeFilter & WuScopeFilter::addOutputStatistic(const char * prop)
  2671. {
  2672. checkModifiable();
  2673. if (!prop)
  2674. return *this;
  2675. StatisticKind kind = queryStatisticKind(prop, StMax);
  2676. if (kind == StMax)
  2677. throw makeStringExceptionV(0, "Unrecognised statistic '%s'", prop);
  2678. return addOutputStatistic(kind);
  2679. }
  2680. WuScopeFilter & WuScopeFilter::addOutputStatistic(StatisticKind stat)
  2681. {
  2682. checkModifiable();
  2683. if (stat != StKindNone)
  2684. {
  2685. if (stat != StKindAll)
  2686. desiredStats.append(stat);
  2687. else
  2688. desiredStats.kill();
  2689. properties |= PTstatistics;
  2690. }
  2691. else
  2692. properties &= ~PTstatistics;
  2693. return *this;
  2694. }
  2695. WuScopeFilter & WuScopeFilter::addOutputAttribute(const char * prop)
  2696. {
  2697. checkModifiable();
  2698. if (!prop)
  2699. return *this;
  2700. WuAttr attr = queryWuAttribute(prop, WaMax);
  2701. if (attr == WaMax)
  2702. throw makeStringExceptionV(0, "Unrecognised attribute '%s'", prop);
  2703. return addOutputAttribute(attr);
  2704. }
  2705. WuScopeFilter & WuScopeFilter::addOutputAttribute(WuAttr attr)
  2706. {
  2707. checkModifiable();
  2708. if (attr != WaNone)
  2709. {
  2710. if (attr != WaAll)
  2711. desiredAttrs.append(attr);
  2712. else
  2713. desiredAttrs.kill();
  2714. properties |= PTattributes;
  2715. }
  2716. else
  2717. properties &= ~PTattributes;
  2718. return *this;
  2719. }
  2720. WuScopeFilter & WuScopeFilter::addOutputHint(const char * prop)
  2721. {
  2722. checkModifiable();
  2723. if (strieq(prop, "none"))
  2724. {
  2725. desiredHints.kill();
  2726. properties &= ~PThints;
  2727. }
  2728. else
  2729. {
  2730. if (!strieq(prop, "all"))
  2731. desiredHints.append(prop);
  2732. else
  2733. desiredHints.kill();
  2734. properties |= PThints;
  2735. }
  2736. return *this;
  2737. }
  2738. WuScopeFilter & WuScopeFilter::setIncludeMatch(bool value)
  2739. {
  2740. checkModifiable();
  2741. include.matchedScope = value;
  2742. return *this;
  2743. }
  2744. WuScopeFilter & WuScopeFilter::setIncludeNesting(unsigned depth)
  2745. {
  2746. checkModifiable();
  2747. include.nestedDepth = depth;
  2748. return *this;
  2749. }
  2750. WuScopeFilter & WuScopeFilter::setIncludeScopeType(const char * scopeType)
  2751. {
  2752. checkModifiable();
  2753. if (scopeType)
  2754. {
  2755. StatisticScopeType sst = queryScopeType(scopeType, SSTmax);
  2756. if (sst == SSTmax)
  2757. throw makeStringExceptionV(0, "Unrecognised scope type '%s'", scopeType);
  2758. include.scopeTypes.append(sst);
  2759. }
  2760. return *this;
  2761. }
  2762. WuScopeFilter & WuScopeFilter::setMeasure(const char * measure)
  2763. {
  2764. checkModifiable();
  2765. if (measure)
  2766. {
  2767. desiredMeasure = queryMeasure(measure, SMeasureNone);
  2768. if (desiredMeasure == SMeasureNone)
  2769. throw makeStringExceptionV(0, "Unrecognised measure '%s'", measure);
  2770. properties |= PTstatistics;
  2771. }
  2772. return *this;
  2773. }
  2774. WuScopeFilter & WuScopeFilter::addOutputProperties(WuPropertyTypes mask)
  2775. {
  2776. checkModifiable();
  2777. if (properties == PTnone)
  2778. properties = mask;
  2779. else
  2780. properties |= mask;
  2781. return *this;
  2782. }
  2783. WuScopeFilter & WuScopeFilter::addRequiredStat(StatisticKind statKind, stat_type lowValue, stat_type highValue)
  2784. {
  2785. checkModifiable();
  2786. requiredStats.emplace_back(statKind, lowValue, highValue);
  2787. return *this;
  2788. }
  2789. WuScopeFilter & WuScopeFilter::addRequiredStat(StatisticKind statKind)
  2790. {
  2791. checkModifiable();
  2792. requiredStats.emplace_back(statKind, 0, MaxStatisticValue);
  2793. return *this;
  2794. }
  2795. WuScopeFilter & WuScopeFilter::addRequiredAttr(WuAttr attr, const char * value)
  2796. {
  2797. checkModifiable();
  2798. requiredAttrs.emplace_back(attr, value);
  2799. return *this;
  2800. }
  2801. //process a filter in one of the following forms:
  2802. // <statistic-name>
  2803. // <statistic-name> (=|<|<=|>|>=) <value>
  2804. // <statistic-name>=[<low>]..[<high>]
  2805. void WuScopeFilter::addRequiredStat(const char * filter)
  2806. {
  2807. checkModifiable();
  2808. const char * stat = filter;
  2809. const char * cur = stat;
  2810. while (isalpha(*cur))
  2811. cur++;
  2812. StringBuffer statisticName(cur-stat, stat);
  2813. //Skip any spaces before a comparison operator.
  2814. while (*cur && isspace(*cur))
  2815. cur++;
  2816. StatisticKind statKind = queryStatisticKind(statisticName, StKindNone);
  2817. if (statKind == StKindNone)
  2818. {
  2819. WuAttr attr = queryWuAttribute(statisticName, WaNone);
  2820. if (attr == WaNone)
  2821. throw makeStringExceptionV(0, "Unknown property name '%s'", statisticName.str());
  2822. if (*cur == '=')
  2823. requiredAttrs.emplace_back(attr, cur+1);
  2824. else if (*cur == '\0')
  2825. requiredAttrs.emplace_back(attr, nullptr);
  2826. else
  2827. throw makeStringExceptionV(0, "Unknown attribute comparison '%s'", cur);
  2828. return;
  2829. }
  2830. //Save the operator, and skip over any non digits.
  2831. const char * op = cur;
  2832. switch (*op)
  2833. {
  2834. case '=':
  2835. cur++;
  2836. break;
  2837. case '<':
  2838. case '>':
  2839. if (op[1] == '=')
  2840. cur += 2;
  2841. else
  2842. cur++;
  2843. break;
  2844. case '\0':
  2845. break;
  2846. default:
  2847. throw makeStringExceptionV(0, "Unknown comparison '%s'", op);
  2848. }
  2849. const char * next;
  2850. stat_type value = readStatisticValue(cur, &next, queryMeasure(statKind));
  2851. stat_type lowValue = 0;
  2852. stat_type highValue = MaxStatisticValue;
  2853. switch (op[0])
  2854. {
  2855. case '=':
  2856. {
  2857. //Allow a,b or a..b to specify a range - either bound may be omitted.
  2858. if (next[0] == ',')
  2859. {
  2860. lowValue = value;
  2861. next++;
  2862. if (*next != '\0')
  2863. highValue = readStatisticValue(next, &next, queryMeasure(statKind));
  2864. }
  2865. else if (strncmp(next, "..", 2) == 0)
  2866. {
  2867. lowValue = value;
  2868. next += 2;
  2869. if (*next != '\0')
  2870. highValue = readStatisticValue(next, &next, queryMeasure(statKind));
  2871. }
  2872. else
  2873. {
  2874. lowValue = value;
  2875. highValue = value;
  2876. }
  2877. break;
  2878. }
  2879. case '<':
  2880. if (op[1] == '=')
  2881. highValue = value;
  2882. else
  2883. highValue = value-1;
  2884. break;
  2885. case '>':
  2886. if (op[1] == '=')
  2887. lowValue = value;
  2888. else
  2889. lowValue = value+1;
  2890. break;
  2891. }
  2892. if (*next)
  2893. throw makeStringExceptionV(0, "Trailing characters in where '%s'", next);
  2894. requiredStats.emplace_back(statKind, lowValue, highValue);
  2895. }
  2896. WuScopeFilter & WuScopeFilter::addSource(const char * source)
  2897. {
  2898. checkModifiable();
  2899. WuScopeSourceFlags mask = querySource(source);
  2900. if (mask == SSFunknown)
  2901. throw makeStringExceptionV(0, "Unexpected source '%s'", source);
  2902. if (!mask)
  2903. sourceFlags = mask;
  2904. else
  2905. sourceFlags |= mask;
  2906. return *this;
  2907. }
  2908. WuScopeFilter & WuScopeFilter::setDepth(unsigned low, unsigned high)
  2909. {
  2910. checkModifiable();
  2911. scopeFilter.setDepth(low, high);
  2912. return *this;
  2913. }
  2914. bool WuScopeFilter::matchOnly(StatisticScopeType scopeType) const
  2915. {
  2916. //If there is a post filter on the scopeType, then check if it matches
  2917. if (include.scopeTypes.ordinality() == 1)
  2918. return (include.scopeTypes.item(0) == scopeType);
  2919. //If filter doesn't match nested items, then check if the scope filter matches.
  2920. if (include.nestedDepth == 0)
  2921. {
  2922. if (scopeFilter.matchOnly(scopeType))
  2923. return true;
  2924. }
  2925. return false;
  2926. }
  2927. void WuScopeFilter::reportModifyTooLate()
  2928. {
  2929. throw makeStringException(WUERR_ModifyFilterAfterFinalize, "Attempting to modify WuScopefilter after finish() has been called");
  2930. }
  2931. //Called once the filter has been updated to optimize the filter
  2932. void WuScopeFilter::finishedFilter()
  2933. {
  2934. if (optimized)
  2935. throw makeStringException(WUERR_FinalizeAfterFinalize, "Calling finishedFilter() more than once");
  2936. scopeFilter.finishedFilter();
  2937. if ((include.nestedDepth == 0) && !include.matchedScope)
  2938. include.nestedDepth = UINT_MAX;
  2939. preFilterScope = include.matchedScope && (include.nestedDepth == 0);
  2940. if (scopeFilter.canAlwaysPreFilter())
  2941. preFilterScope = true;
  2942. //If the source flags have not been explicitly set then calculate which sources will provide the results
  2943. if (!sourceFlags)
  2944. {
  2945. sourceFlags = SSFsearchAll;
  2946. //Use the other options to reduce the number of elements that are searched.
  2947. //If not interested in scopes on their own then...
  2948. if (!(properties & PTscope))
  2949. {
  2950. //Global stats and graph stats only contain stats => remove if not interested in them
  2951. if (!(properties & PTstatistics) && (requiredStats.size() == 0))
  2952. sourceFlags &= ~(SSFsearchGlobalStats|SSFsearchGraphStats);
  2953. //graph, workflow only contains attributes and hints => remove if not interested
  2954. if (!(properties & (PTattributes|PThints)) && (requiredAttrs.size() == 0))
  2955. sourceFlags &= ~(SSFsearchGraph|SSFsearchWorkflow);
  2956. }
  2957. if (!(properties & PTnotes))
  2958. sourceFlags &= ~SSFsearchExceptions;
  2959. }
  2960. //Optimize sources if they haven't been explicitly specified
  2961. //Most of the following are dependent on internal knowledge of the way that information is represented
  2962. if (matchOnly(SSTworkflow))
  2963. {
  2964. //Workflow is not nested within a graph
  2965. sourceFlags &= ~(SSFsearchGraph|SSFsearchGraphStats);
  2966. setDepth(1, 1);
  2967. }
  2968. else if (matchOnly(SSTgraph))
  2969. {
  2970. //Graph starts are stored globally, not in the graph stats.
  2971. sourceFlags &= ~(SSFsearchGraphStats|SSFsearchWorkflow);
  2972. if (!(properties & (PTattributes|PThints)))
  2973. sourceFlags &= ~(SSFsearchGraph);
  2974. //This should really be setDepth(2,2) but workunits prior to 7.4 did not have graph ids prefixed by the wfid
  2975. //Remove once 7.2 is a distant memory (see HPCC-22887)
  2976. setDepth(1, 2);
  2977. }
  2978. else if (matchOnly(SSTsubgraph))
  2979. {
  2980. sourceFlags &= ~(SSFsearchWorkflow);
  2981. }
  2982. else if (matchOnly(SSTcompilestage))
  2983. {
  2984. //compile stages are not stored in the graph
  2985. sourceFlags &= ~(SSFsearchGraphStats|SSFsearchGraph|SSFsearchWorkflow);
  2986. }
  2987. else if (matchOnly(SSTactivity))
  2988. {
  2989. //information about activities is not stored globally
  2990. sourceFlags &= ~(SSFsearchGlobalStats|SSFsearchWorkflow);
  2991. }
  2992. // Everything stored in the graphs stats has a depth of 3 or more - so ignore if there are not matches in that depth
  2993. if (include.nestedDepth == 0)
  2994. {
  2995. if (scopeFilter.compareDepth(3) > 0) // 3 is larger than any scope in the filter
  2996. sourceFlags &= ~(SSFsearchGraphStats);
  2997. else if (scopeFilter.compareDepth(3) == 0) // 3 matches the maximum scope in the filter
  2998. {
  2999. //Subgraph WhenStarted and TimeElapsed are stored globally. If that is all that is required
  3000. //then do not include the subgraph timings
  3001. if (desiredStats.ordinality())
  3002. {
  3003. bool needGraphStats = false;
  3004. ForEachItemIn(i, desiredStats)
  3005. {
  3006. unsigned stat = desiredStats.item(i);
  3007. if ((stat != StWhenStarted) && (stat != StTimeElapsed))
  3008. needGraphStats = true;
  3009. }
  3010. if (!needGraphStats)
  3011. {
  3012. sourceFlags &= ~(SSFsearchGraphStats);
  3013. if (matchOnly(SSTsubgraph))
  3014. sourceFlags &= SSFsearchGlobalStats;
  3015. }
  3016. }
  3017. }
  3018. }
  3019. if (scopeFilter.compareDepth(1) < 0)
  3020. {
  3021. //If minimum match depth is > 1 then it will never match workflow
  3022. sourceFlags &= ~(SSFsearchWorkflow);
  3023. }
  3024. //The xml graph is never updated, so do not check it if minVersion != 0
  3025. if (minVersion != 0)
  3026. {
  3027. sourceFlags &= ~(SSFsearchGraph|SSFsearchWorkflow);
  3028. }
  3029. optimized = true;
  3030. }
  3031. bool WuScopeFilter::includeStatistic(StatisticKind kind) const
  3032. {
  3033. if (!(properties & PTstatistics))
  3034. return false;
  3035. if ((desiredMeasure != SMeasureAll) && (queryMeasure(kind) != desiredMeasure))
  3036. return false;
  3037. if (desiredStats.empty())
  3038. return true;
  3039. return desiredStats.contains(kind);
  3040. }
  3041. bool WuScopeFilter::includeAttribute(WuAttr attr) const
  3042. {
  3043. if (!(properties & PTattributes))
  3044. return false;
  3045. if (desiredAttrs.empty())
  3046. return true;
  3047. return desiredAttrs.contains(attr);
  3048. }
  3049. bool WuScopeFilter::includeHint(const char * kind) const
  3050. {
  3051. if (!(properties & PThints))
  3052. return false;
  3053. if (desiredHints.empty())
  3054. return true;
  3055. return desiredHints.contains(kind);
  3056. }
  3057. bool WuScopeFilter::includeScope(const char * scope) const
  3058. {
  3059. if (include.scopeTypes)
  3060. {
  3061. const char * tail = queryScopeTail(scope);
  3062. StatsScopeId id(tail);
  3063. if (!include.scopeTypes.contains(id.queryScopeType()))
  3064. return false;
  3065. }
  3066. return true;
  3067. }
  3068. const static ScopeFilter nullScopeFilter {};
  3069. const ScopeFilter & WuScopeFilter::queryIterFilter() const
  3070. {
  3071. if (preFilterScope)
  3072. return scopeFilter;
  3073. else
  3074. return nullScopeFilter;
  3075. }
  3076. ScopeCompare WuScopeFilter::compareMatchScopes(const char * scope) const
  3077. {
  3078. return scopeFilter.compare(scope);
  3079. }
  3080. StringBuffer & WuScopeFilter::describe(StringBuffer & out) const
  3081. {
  3082. scopeFilter.describe(out);
  3083. if (requiredStats.size() || requiredAttrs.size())
  3084. {
  3085. out.append(",where[");
  3086. bool first = false;
  3087. for (const auto & stat : requiredStats)
  3088. {
  3089. if (!first)
  3090. out.append(",");
  3091. stat.describe(out);
  3092. first = false;
  3093. }
  3094. for (const auto & attr : requiredAttrs)
  3095. {
  3096. if (!first)
  3097. out.append(",");
  3098. attr.describe(out);
  3099. first = false;
  3100. }
  3101. out.append("]");
  3102. }
  3103. {
  3104. StringBuffer sources;
  3105. for (unsigned mask=1; mask; mask *= 2)
  3106. {
  3107. if (sourceFlags & mask)
  3108. {
  3109. const char * source = querySourceText((WuScopeSourceFlags)mask);
  3110. if (source)
  3111. sources.append(",").append(source);
  3112. }
  3113. }
  3114. if (sources)
  3115. out.append(",source[").append(sources.str()+1).append("]");
  3116. }
  3117. if (include.nestedDepth != UINT_MAX)
  3118. out.appendf(",nested[%u]", include.nestedDepth);
  3119. if (include.scopeTypes)
  3120. {
  3121. out.append(",include[");
  3122. ForEachItemIn(i, include.scopeTypes)
  3123. {
  3124. if (i)
  3125. out.append(",");
  3126. out.append(queryScopeTypeName((StatisticScopeType)include.scopeTypes.item(i)));
  3127. }
  3128. out.append("]");
  3129. }
  3130. {
  3131. StringBuffer props;
  3132. if (properties == PTnone)
  3133. props.append(",none");
  3134. else if (properties == PTall)
  3135. props.append(",all");
  3136. else
  3137. {
  3138. if (properties & PTstatistics)
  3139. props.append(",stat");
  3140. if (properties & PTattributes)
  3141. props.append(",attr");
  3142. if (properties & PThints)
  3143. props.append(",hint");
  3144. if (properties & PTscope)
  3145. props.append(",scope");
  3146. if (properties & PTnotes)
  3147. props.append(",note");
  3148. }
  3149. out.append(",properties[").append(props.str()+1).append("]");
  3150. }
  3151. if (desiredStats)
  3152. {
  3153. out.append(",stat[");
  3154. ForEachItemIn(i, desiredStats)
  3155. {
  3156. if (i)
  3157. out.append(",");
  3158. out.append(queryStatisticName((StatisticKind)desiredStats.item(i)));
  3159. }
  3160. out.append("]");
  3161. }
  3162. if (desiredAttrs)
  3163. {
  3164. out.append(",attr[");
  3165. ForEachItemIn(i, desiredAttrs)
  3166. {
  3167. if (i)
  3168. out.append(",");
  3169. out.append(queryWuAttributeName((WuAttr)desiredAttrs.item(i)));
  3170. }
  3171. out.append("]");
  3172. }
  3173. if (desiredHints)
  3174. {
  3175. out.append(",hint[");
  3176. ForEachItemIn(i, desiredHints)
  3177. {
  3178. if (i)
  3179. out.append(",");
  3180. out.append(desiredHints.item(i));
  3181. }
  3182. out.append("]");
  3183. }
  3184. if (desiredMeasure != SMeasureAll)
  3185. out.append(",measure[").append(queryMeasureName(desiredMeasure)).append("]");
  3186. if (minVersion != 0)
  3187. out.appendf(",version(%" I64F "u)", minVersion);
  3188. return out;
  3189. }
  3190. //--------------------------------------------------------------------------------------------------------------------
  3191. EnumMapping states[] = {
  3192. { WUStateUnknown, "unknown" },
  3193. { WUStateCompiled, "compiled" },
  3194. { WUStateRunning, "running" },
  3195. { WUStateCompleted, "completed" },
  3196. { WUStateFailed, "failed" },
  3197. { WUStateArchived, "archived" },
  3198. { WUStateAborting, "aborting" },
  3199. { WUStateAborted, "aborted" },
  3200. { WUStateBlocked, "blocked" },
  3201. { WUStateSubmitted, "submitted" },
  3202. { WUStateScheduled, "scheduled" },
  3203. { WUStateCompiling, "compiling" },
  3204. { WUStateWait, "wait" },
  3205. { WUStateUploadingFiles, "uploading_files" },
  3206. { WUStateDebugPaused, "debugging" },
  3207. { WUStateDebugRunning, "debug_running" },
  3208. { WUStatePaused, "paused" },
  3209. { WUStateSize, NULL }
  3210. };
  3211. EnumMapping actions[] = {
  3212. { WUActionUnknown, "unknown" },
  3213. { WUActionCompile, "compile" },
  3214. { WUActionCheck, "check" },
  3215. { WUActionRun, "run" },
  3216. { WUActionExecuteExisting, "execute" },
  3217. { WUActionPause, "pause" },
  3218. { WUActionPauseNow, "pausenow" },
  3219. { WUActionResume, "resume" },
  3220. { WUActionSize, NULL },
  3221. };
  3222. EnumMapping priorityClasses[] = {
  3223. { PriorityClassUnknown, "unknown" },
  3224. { PriorityClassLow, "low" },
  3225. { PriorityClassNormal, "normal" },
  3226. { PriorityClassHigh, "high" },
  3227. { PriorityClassSize, NULL },
  3228. };
  3229. const char * getWorkunitStateStr(WUState state)
  3230. {
  3231. dbgassertex(state < WUStateSize);
  3232. return states[state].str; // MORE - should be using getEnumText, or need to take steps to ensure values remain contiguous and in order.
  3233. }
  3234. void setEnum(IPropertyTree *p, const char *propname, int value, const EnumMapping *map)
  3235. {
  3236. const char *defval = map->str;
  3237. while (map->str)
  3238. {
  3239. if (value==map->val)
  3240. {
  3241. p->setProp(propname, map->str);
  3242. return;
  3243. }
  3244. map++;
  3245. }
  3246. assertex(!"Unexpected value in setEnum");
  3247. p->setProp(propname, defval);
  3248. }
  3249. static int getEnum(const IPropertyTree *p, const char *propname, const EnumMapping *map)
  3250. {
  3251. return getEnum(p->queryProp(propname),map);
  3252. }
  3253. const char * getWorkunitActionStr(WUAction action)
  3254. {
  3255. return getEnumText(action, actions);
  3256. }
  3257. WUAction getWorkunitAction(const char *actionStr)
  3258. {
  3259. return (WUAction) getEnum(actionStr, actions);
  3260. }
  3261. //==========================================================================================
  3262. class CLightweightWorkunitInfo : public CInterfaceOf<IConstWorkUnitInfo>
  3263. {
  3264. public:
  3265. CLightweightWorkunitInfo(IPropertyTree &p)
  3266. {
  3267. wuid.set(p.queryName());
  3268. user.set(p.queryProp("@submitID"));
  3269. jobName.set(p.queryProp("@jobName"));
  3270. clusterName.set(p.queryProp("@clusterName"));
  3271. timeScheduled.set(p.queryProp("@timeScheduled"));
  3272. state = (WUState) getEnum(&p, "@state", states);
  3273. action = (WUAction) getEnum(&p, "Action", actions);
  3274. priority = (WUPriorityClass) getEnum(&p, "@priorityClass", priorityClasses);
  3275. priorityLevel = calcPriorityValue(&p);
  3276. wuscope.set(p.queryProp("@scope"));
  3277. appvalues.loadBranch(&p,"Application");
  3278. totalThorTime = (unsigned)nanoToMilli(extractTimeCollatable(p.queryProp("@totalThorTime"), nullptr));
  3279. _isProtected = p.getPropBool("@protected", false);
  3280. costExecute = p.getPropInt64("@costExecute");
  3281. costFileAccess = p.getPropInt64("@costFileAccess");
  3282. }
  3283. virtual const char *queryWuid() const { return wuid.str(); }
  3284. virtual const char *queryUser() const { return user.str(); }
  3285. virtual const char *queryJobName() const { return jobName.str(); }
  3286. virtual const char *queryClusterName() const { return clusterName.str(); }
  3287. virtual const char *queryWuScope() const { return wuscope.str(); }
  3288. virtual WUState getState() const { return state; }
  3289. virtual const char *queryStateDesc() const { return getEnumText(state, states); }
  3290. virtual WUAction getAction() const { return action; }
  3291. virtual const char *queryActionDesc() const { return getEnumText(action, actions); }
  3292. virtual WUPriorityClass getPriority() const { return priority; }
  3293. virtual const char *queryPriorityDesc() const { return getEnumText(priority, priorityClasses); }
  3294. virtual int getPriorityLevel() const { return priorityLevel; }
  3295. virtual bool isProtected() const { return _isProtected; }
  3296. virtual cost_type getExecuteCost() const { return costExecute; }
  3297. virtual cost_type getFileAccessCost() const { return costFileAccess; }
  3298. virtual IJlibDateTime & getTimeScheduled(IJlibDateTime & val) const
  3299. {
  3300. if (timeScheduled.length())
  3301. val.setGmtString(timeScheduled.str());
  3302. return val;
  3303. }
  3304. virtual unsigned getTotalThorTime() const { return totalThorTime; };
  3305. virtual IConstWUAppValueIterator & getApplicationValues() const { return *new CArrayIteratorOf<IConstWUAppValue,IConstWUAppValueIterator> (appvalues, 0, (IConstWorkUnitInfo *) this); };
  3306. protected:
  3307. StringAttr wuid, user, jobName, clusterName, timeScheduled, wuscope;
  3308. mutable CachedWUAppValues appvalues;
  3309. unsigned totalThorTime;
  3310. WUState state;
  3311. WUAction action;
  3312. WUPriorityClass priority;
  3313. int priorityLevel;
  3314. bool _isProtected;
  3315. unsigned __int64 costExecute;
  3316. unsigned __int64 costFileAccess;
  3317. };
  3318. extern IConstWorkUnitInfo *createConstWorkUnitInfo(IPropertyTree &p)
  3319. {
  3320. return new CLightweightWorkunitInfo(p);
  3321. }
  3322. class CDaliWorkUnit;
  3323. class CDaliWuGraphStats : public CWuGraphStats
  3324. {
  3325. public:
  3326. CDaliWuGraphStats(const CDaliWorkUnit* _owner, StatisticCreatorType _creatorType, const char * _creator, unsigned _wfid, const char * _rootScope, unsigned _id, bool _merge)
  3327. : CWuGraphStats(_creatorType, _creator, _wfid, _rootScope, _id, _merge), owner(_owner), graphName(_rootScope), wfid(_wfid)
  3328. {
  3329. }
  3330. protected:
  3331. virtual IPropertyTree &queryProgressTree() override;
  3332. const CDaliWorkUnit *owner;
  3333. Owned<IRemoteConnection> conn;
  3334. StringAttr graphName;
  3335. unsigned wfid;
  3336. };
  3337. class CLocalWuGraphStats : public CWuGraphStats
  3338. {
  3339. public:
  3340. CLocalWuGraphStats(IPropertyTree *_p, StatisticCreatorType _creatorType, const char * _creator, unsigned _wfid, const char * _rootScope, unsigned _id, bool _merge)
  3341. : CWuGraphStats(_creatorType, _creator, _wfid, _rootScope, _id, _merge), graphName(_rootScope), p(_p)
  3342. {
  3343. }
  3344. protected:
  3345. virtual IPropertyTree &queryProgressTree() override
  3346. {
  3347. IPropertyTree *progress = p->queryPropTree("GraphProgress");
  3348. if (!progress)
  3349. progress = p->addPropTree("GraphProgress");
  3350. IPropertyTree *graph = progress->queryPropTree(graphName);
  3351. if (!graph)
  3352. graph = progress->addPropTree(graphName);
  3353. return *graph;
  3354. }
  3355. StringAttr graphName;
  3356. Owned<IPropertyTree> p;
  3357. };
  3358. CWorkUnitWatcher::CWorkUnitWatcher(IWorkUnitSubscriber *_subscriber, WUSubscribeOptions flags, const char *wuid) : subscriber(_subscriber)
  3359. {
  3360. abortId = 0;
  3361. stateId = 0;
  3362. actionId = 0;
  3363. assertex((flags & ~SubscribeOptionAbort) == 0);
  3364. if (flags & SubscribeOptionAbort)
  3365. {
  3366. VStringBuffer xpath("/WorkUnitAborts/%s", wuid);
  3367. abortId = querySDS().subscribe(xpath.str(), *this, false, true);
  3368. }
  3369. }
  3370. CWorkUnitWatcher::~CWorkUnitWatcher()
  3371. {
  3372. assertex(abortId==0 && stateId==0 && actionId==0);
  3373. }
  3374. void CWorkUnitWatcher::unsubscribe()
  3375. {
  3376. CriticalBlock b(crit);
  3377. if (abortId)
  3378. querySDS().unsubscribe(abortId);
  3379. if (stateId)
  3380. querySDS().unsubscribe(stateId);
  3381. if (actionId)
  3382. querySDS().unsubscribe(actionId);
  3383. abortId = 0;
  3384. stateId = 0;
  3385. actionId = 0;
  3386. }
  3387. void CWorkUnitWatcher::notify(SubscriptionId id, const char *xpath, SDSNotifyFlags flags, unsigned valueLen, const void *valueData)
  3388. {
  3389. CriticalBlock b(crit);
  3390. if (id==stateId)
  3391. subscriber->notify(SubscribeOptionState, valueLen, valueData);
  3392. else if (id==actionId)
  3393. subscriber->notify(SubscribeOptionAction, valueLen, valueData);
  3394. else if (id==abortId)
  3395. subscriber->notify(SubscribeOptionAbort, valueLen, valueData);
  3396. }
  3397. class CDaliWorkUnitWatcher : public CWorkUnitWatcher
  3398. {
  3399. public:
  3400. CDaliWorkUnitWatcher(IWorkUnitSubscriber *_subscriber, WUSubscribeOptions flags, const char *wuid)
  3401. : CWorkUnitWatcher(_subscriber, (WUSubscribeOptions) (flags & SubscribeOptionAbort), wuid)
  3402. {
  3403. if (flags & SubscribeOptionState)
  3404. {
  3405. VStringBuffer xpath("/WorkUnits/%s/State", wuid);
  3406. stateId = querySDS().subscribe(xpath.str(), *this);
  3407. }
  3408. if (flags & SubscribeOptionAction)
  3409. {
  3410. VStringBuffer xpath("/WorkUnits/%s/Action", wuid);
  3411. actionId = querySDS().subscribe(xpath.str(), *this);
  3412. }
  3413. }
  3414. };
  3415. void CPersistedWorkUnit::subscribe(WUSubscribeOptions options)
  3416. {
  3417. CriticalBlock block(crit);
  3418. assertex(options==SubscribeOptionAbort);
  3419. if (!abortWatcher)
  3420. {
  3421. abortWatcher.setown(new CWorkUnitWatcher(this, SubscribeOptionAbort, p->queryName()));
  3422. abortDirty = true;
  3423. }
  3424. }
  3425. void CPersistedWorkUnit::unsubscribe()
  3426. {
  3427. CriticalBlock block(crit);
  3428. if (abortWatcher)
  3429. {
  3430. abortWatcher->unsubscribe();
  3431. abortWatcher.clear();
  3432. }
  3433. }
  3434. bool CPersistedWorkUnit::aborting() const
  3435. {
  3436. CriticalBlock block(crit);
  3437. if (abortDirty)
  3438. {
  3439. StringBuffer apath;
  3440. apath.append("/WorkUnitAborts/").append(p->queryName());
  3441. Owned<IRemoteConnection> acon = querySDS().connect(apath.str(), myProcessSession(), 0, SDS_LOCK_TIMEOUT);
  3442. if (acon)
  3443. abortState = acon->queryRoot()->getPropInt(NULL) != 0;
  3444. else
  3445. abortState = false;
  3446. abortDirty = false;
  3447. }
  3448. return abortState;
  3449. }
  3450. class CDaliWorkUnit : public CPersistedWorkUnit
  3451. {
  3452. friend class CDaliWuGraphStats;
  3453. public:
  3454. CDaliWorkUnit(IRemoteConnection *_conn, ISecManager *secmgr, ISecUser *secuser)
  3455. : connection(_conn), CPersistedWorkUnit(secmgr, secuser)
  3456. {
  3457. loadPTree(connection->getRoot());
  3458. }
  3459. ~CDaliWorkUnit()
  3460. {
  3461. // NOTE - order is important - we need to construct connection before p and (especially) destroy after p
  3462. // We use the beforeDispose() in base class to help ensure this
  3463. p.clear();
  3464. }
  3465. IConstWUGraphProgress *getGraphProgress(const char *graphName) const
  3466. {
  3467. Owned<IRemoteConnection> conn = getProgressConnection();
  3468. if (conn)
  3469. {
  3470. IPTree *progress = conn->queryRoot()->queryPropTree(graphName);
  3471. if (progress)
  3472. return new CConstGraphProgress(p->queryName(), graphName, progress);
  3473. }
  3474. return NULL;
  3475. }
  3476. virtual WUGraphState queryGraphState(const char *graphName) const
  3477. {
  3478. Owned<IRemoteConnection> conn = getProgressConnection();
  3479. if (conn)
  3480. {
  3481. IPTree *progress = conn->queryRoot()->queryPropTree(graphName);
  3482. if (progress)
  3483. return (WUGraphState) progress->getPropInt("@_state", (unsigned) WUGraphUnknown);
  3484. }
  3485. return WUGraphUnknown;
  3486. }
  3487. virtual WUGraphState queryNodeState(const char *graphName, WUGraphIDType nodeId) const
  3488. {
  3489. Owned<IRemoteConnection> conn = getProgressConnection();
  3490. if (conn)
  3491. {
  3492. IPTree *progress = conn->queryRoot()->queryPropTree(graphName);
  3493. if (progress)
  3494. {
  3495. StringBuffer path;
  3496. // NOTE - the node state info still uses the old graph layout, even when the stats are using the new...
  3497. path.append("node[@id=\"").append(nodeId).append("\"]/@_state");
  3498. return (WUGraphState) progress->getPropInt(path, (unsigned) WUGraphUnknown);
  3499. }
  3500. }
  3501. return WUGraphUnknown;
  3502. }
  3503. virtual void clearGraphProgress() const
  3504. {
  3505. CriticalBlock block(crit);
  3506. progressConnection.clear(); // Make sure nothing is locking for read or we won't be able to lock for write
  3507. StringBuffer path("/GraphProgress/");
  3508. path.append(p->queryName());
  3509. Owned<IRemoteConnection> delconn = querySDS().connect(path.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  3510. if (delconn)
  3511. delconn->close(true);
  3512. }
  3513. virtual bool getRunningGraph(IStringVal &graphName, WUGraphIDType &subId) const
  3514. {
  3515. Owned<IRemoteConnection> conn = getProgressConnection();
  3516. if (!conn)
  3517. return false;
  3518. const char *name = conn->queryRoot()->queryProp("Running/@graph");
  3519. if (name)
  3520. {
  3521. graphName.set(name);
  3522. subId = conn->queryRoot()->getPropInt64("Running/@subId");
  3523. return true;
  3524. }
  3525. else
  3526. return false;
  3527. }
  3528. virtual void forceReload()
  3529. {
  3530. synchronized sync(locked); // protect locked workunits (uncommitted writes) from reload
  3531. CriticalBlock block(crit);
  3532. clearCached(true);
  3533. connection->reload();
  3534. progressConnection.clear();
  3535. abortDirty = true;
  3536. p.setown(connection->getRoot());
  3537. }
  3538. virtual void cleanupAndDelete(bool deldll, bool deleteOwned, const StringArray *deleteExclusions)
  3539. {
  3540. CPersistedWorkUnit::cleanupAndDelete(deldll, deleteOwned, deleteExclusions);
  3541. clearGraphProgress();
  3542. connection->close(true);
  3543. connection.clear();
  3544. }
  3545. virtual void commit()
  3546. {
  3547. CPersistedWorkUnit::commit();
  3548. if (connection)
  3549. connection->commit();
  3550. }
  3551. virtual void _lockRemote()
  3552. {
  3553. StringBuffer wuRoot;
  3554. getXPath(wuRoot, p->queryName());
  3555. if (connection)
  3556. connection->changeMode(RTM_LOCK_WRITE,SDS_LOCK_TIMEOUT);
  3557. else
  3558. connection.setown(querySDS().connect(wuRoot.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
  3559. if (!connection)
  3560. throw MakeStringException(WUERR_LockFailed, "Failed to get connection for xpath %s", wuRoot.str());
  3561. clearCached(true);
  3562. p.setown(connection->getRoot());
  3563. }
  3564. virtual void _unlockRemote()
  3565. {
  3566. if (connection)
  3567. {
  3568. try
  3569. {
  3570. try
  3571. {
  3572. connection->commit();
  3573. }
  3574. catch (IException *e)
  3575. {
  3576. EXCLOG(e, "Error during workunit commit");
  3577. connection->rollback();
  3578. connection->changeMode(0, SDS_LOCK_TIMEOUT);
  3579. throw;
  3580. }
  3581. connection->changeMode(0, SDS_LOCK_TIMEOUT);
  3582. }
  3583. catch (IException *E)
  3584. {
  3585. StringBuffer s;
  3586. IERRLOG("Failed to release write lock on workunit: %s", E->errorMessage(s).str());
  3587. throw;
  3588. }
  3589. }
  3590. }
  3591. virtual void setGraphState(const char *graphName, unsigned wfid, WUGraphState state) const
  3592. {
  3593. Owned<IRemoteConnection> conn = getWritableProgressConnection(graphName, wfid);
  3594. conn->queryRoot()->setPropInt("@_state", state);
  3595. }
  3596. virtual void setNodeState(const char *graphName, WUGraphIDType nodeId, WUGraphState state) const
  3597. {
  3598. CriticalBlock block(crit);
  3599. progressConnection.clear(); // Make sure nothing is locking for read or we won't be able to lock for write
  3600. VStringBuffer path("/GraphProgress/%s", queryWuid());
  3601. Owned<IRemoteConnection> conn = querySDS().connect(path, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  3602. IPTree *progress = ensurePTree(conn->queryRoot(), graphName);
  3603. // NOTE - the node state info still uses the old graph layout, even when the stats are using the new...
  3604. path.clear().append("node[@id=\"").append(nodeId).append("\"]");
  3605. IPropertyTree *node = progress->queryPropTree(path.str());
  3606. if (!node)
  3607. {
  3608. node = progress->addPropTree("node");
  3609. node->setPropInt64("@id", nodeId);
  3610. }
  3611. node->setPropInt("@_state", (unsigned)state);
  3612. switch (state)
  3613. {
  3614. case WUGraphRunning:
  3615. {
  3616. IPropertyTree *running = conn->queryRoot()->setPropTree("Running");
  3617. running->setProp("@graph", graphName);
  3618. running->setPropInt64("@subId", nodeId);
  3619. break;
  3620. }
  3621. case WUGraphComplete:
  3622. {
  3623. conn->queryRoot()->removeProp("Running"); // only one thing running at any given time and one thing with lockWrite access
  3624. break;
  3625. }
  3626. }
  3627. }
  3628. virtual IWUGraphStats *updateStats(const char *graphName, StatisticCreatorType creatorType, const char * creator, unsigned _wfid, unsigned subgraph, bool merge) const override
  3629. {
  3630. return new CDaliWuGraphStats(this, creatorType, creator, _wfid, graphName, subgraph, merge);
  3631. }
  3632. virtual void import(IPropertyTree *wuTree, IPropertyTree *graphProgressTree)
  3633. {
  3634. connection->queryRoot()->setPropTree(nullptr, LINK(wuTree));
  3635. loadPTree(connection->getRoot());
  3636. if (!graphProgressTree)
  3637. return;
  3638. VStringBuffer xpath("/GraphProgress/%s", queryWuid());
  3639. Owned<IRemoteConnection> progressConn = querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE, SDS_LOCK_TIMEOUT);
  3640. if (!progressConn)
  3641. throw MakeStringException(0, "Failed to access %s.", xpath.str());
  3642. progressConn->queryRoot()->setPropTree(nullptr, LINK(graphProgressTree));
  3643. }
  3644. protected:
  3645. IRemoteConnection *getProgressConnection() const
  3646. {
  3647. CriticalBlock block(crit);
  3648. if (!progressConnection)
  3649. {
  3650. VStringBuffer path("/GraphProgress/%s", queryWuid());
  3651. progressConnection.setown(querySDS().connect(path, myProcessSession(), 0, SDS_LOCK_TIMEOUT)); // Note - we don't lock. The writes are atomic.
  3652. }
  3653. return progressConnection.getLink();
  3654. }
  3655. IRemoteConnection *getWritableProgressConnection(const char *graphName, unsigned wfid) const
  3656. {
  3657. CriticalBlock block(crit);
  3658. progressConnection.clear(); // Make sure subsequent reads from this workunit get the changes I am making
  3659. VStringBuffer path("/GraphProgress/%s/%s", queryWuid(), graphName);
  3660. Owned<IRemoteConnection> conn = querySDS().connect(path, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  3661. IPropertyTree * root = conn->queryRoot();
  3662. assertex(wfid);
  3663. if (!root->hasProp("@wfid"))
  3664. {
  3665. root->setPropInt("@wfid", wfid);
  3666. }
  3667. else
  3668. {
  3669. //Ideally the following code would check that the wfids are passed consistently.
  3670. //However there is an obscure problem with out of line functions being called from multiple workflow
  3671. //ids, and possibly library graphs.
  3672. //Stats for library graphs should be nested below the library call activity
  3673. //assertex(root->getPropInt("@wfid", 0) == wfid); // check that wfid is passed consistently
  3674. }
  3675. return conn.getClear();
  3676. }
  3677. IPropertyTree *getGraphProgressTree() const
  3678. {
  3679. Owned<IRemoteConnection> conn = getProgressConnection();
  3680. if (conn)
  3681. {
  3682. Owned<IPropertyTree> tmp = createPTree("GraphProgress");
  3683. mergePTree(tmp,conn->queryRoot());
  3684. return tmp.getClear();
  3685. }
  3686. return NULL;
  3687. }
  3688. Owned<IRemoteConnection> connection;
  3689. mutable Owned<IRemoteConnection> progressConnection;
  3690. };
  3691. class CLockedWorkUnit : implements ILocalWorkUnit, implements IExtendedWUInterface, public CInterface
  3692. {
  3693. public:
  3694. Owned<CLocalWorkUnit> c;
  3695. IMPLEMENT_IINTERFACE;
  3696. CLockedWorkUnit(CLocalWorkUnit *_c) : c(_c) {}
  3697. ~CLockedWorkUnit()
  3698. {
  3699. if (workUnitTraceLevel > 1)
  3700. DBGLOG("Releasing locked workunit %s", queryWuid());
  3701. if (c)
  3702. {
  3703. try
  3704. {
  3705. c->unlockRemote();
  3706. }
  3707. catch (IException *E)
  3708. {
  3709. // Exceptions here should be very uncommon - but there's also not a lot we can do if we get one
  3710. // Allowing them to be thrown out of the destructor is going to terminate the program which is NOT what we want.
  3711. EXCLOG(E);
  3712. ::Release(E);
  3713. }
  3714. }
  3715. }
  3716. virtual IConstWorkUnit * unlock() override
  3717. {
  3718. c->unlockRemote();
  3719. return c.getClear();
  3720. }
  3721. virtual bool aborting() const
  3722. { return c->aborting(); }
  3723. virtual void forceReload()
  3724. { }
  3725. virtual WUAction getAction() const
  3726. { return c->getAction(); }
  3727. virtual const char *queryActionDesc() const
  3728. { return c->queryActionDesc(); }
  3729. virtual IStringVal & getApplicationValue(const char * application, const char * propname, IStringVal & str) const
  3730. { return c->getApplicationValue(application, propname, str); }
  3731. virtual int getApplicationValueInt(const char * application, const char * propname, int defVal) const
  3732. { return c->getApplicationValueInt(application, propname, defVal); }
  3733. virtual IConstWUAppValueIterator & getApplicationValues() const
  3734. { return c->getApplicationValues(); }
  3735. virtual bool hasWorkflow() const
  3736. { return c->hasWorkflow(); }
  3737. virtual unsigned queryEventScheduledCount() const
  3738. { return c->queryEventScheduledCount(); }
  3739. virtual IPropertyTree * queryWorkflowTree() const
  3740. { return c->queryWorkflowTree(); }
  3741. virtual IConstWorkflowItemIterator * getWorkflowItems() const
  3742. { return c->getWorkflowItems(); }
  3743. virtual IWorkflowItemArray * getWorkflowClone() const
  3744. { return c->getWorkflowClone(); }
  3745. virtual bool requiresLocalFileUpload() const
  3746. { return c->requiresLocalFileUpload(); }
  3747. virtual IConstLocalFileUploadIterator * getLocalFileUploads() const
  3748. { return c->getLocalFileUploads(); }
  3749. virtual bool getIsQueryService() const
  3750. { return c->getIsQueryService(); }
  3751. virtual bool getCloneable() const
  3752. { return c->getCloneable(); }
  3753. virtual IUserDescriptor * queryUserDescriptor() const
  3754. { return c->queryUserDescriptor(); }
  3755. virtual const char *queryClusterName() const
  3756. { return c->queryClusterName(); }
  3757. virtual unsigned getCodeVersion() const
  3758. { return c->getCodeVersion(); }
  3759. virtual unsigned getWuidVersion() const
  3760. { return c->getWuidVersion(); }
  3761. virtual void getBuildVersion(IStringVal & buildVersion, IStringVal & eclVersion) const
  3762. { c->getBuildVersion(buildVersion, eclVersion); }
  3763. virtual bool hasDebugValue(const char * propname) const
  3764. { return c->hasDebugValue(propname); }
  3765. virtual IStringVal & getDebugValue(const char * propname, IStringVal & str) const
  3766. { return c->getDebugValue(propname, str); }
  3767. virtual int getDebugValueInt(const char * propname, int defVal) const
  3768. { return c->getDebugValueInt(propname, defVal); }
  3769. virtual __int64 getDebugValueInt64(const char * propname, __int64 defVal) const
  3770. { return c->getDebugValueInt64(propname, defVal); }
  3771. virtual double getDebugValueReal(const char * propname, double defVal) const
  3772. { return c->getDebugValueReal(propname, defVal); }
  3773. virtual bool getDebugValueBool(const char * propname, bool defVal) const
  3774. { return c->getDebugValueBool(propname, defVal); }
  3775. virtual IStringIterator & getDebugValues() const
  3776. { return c->getDebugValues(NULL); }
  3777. virtual IStringIterator & getDebugValues(const char *prop) const
  3778. { return c->getDebugValues(prop); }
  3779. virtual unsigned getExceptionCount() const
  3780. { return c->getExceptionCount(); }
  3781. virtual IConstWUExceptionIterator & getExceptions() const
  3782. { return c->getExceptions(); }
  3783. virtual unsigned getGraphCount() const
  3784. { return c->getGraphCount(); }
  3785. virtual unsigned getSourceFileCount() const
  3786. { return c->getSourceFileCount(); }
  3787. virtual unsigned getResultCount() const
  3788. { return c->getResultCount(); }
  3789. virtual unsigned getVariableCount() const
  3790. { return c->getVariableCount(); }
  3791. virtual unsigned getApplicationValueCount() const
  3792. { return c->getApplicationValueCount(); }
  3793. virtual IConstWUGraphIterator & getGraphs(WUGraphType type) const
  3794. { return c->getGraphs(type); }
  3795. virtual IConstWUGraphMetaIterator & getGraphsMeta(WUGraphType type) const
  3796. { return c->getGraphsMeta(type); }
  3797. virtual IConstWUGraph * getGraph(const char *name) const
  3798. { return c->getGraph(name); }
  3799. virtual IConstWUGraphProgress * getGraphProgress(const char * name) const
  3800. { return c->getGraphProgress(name); }
  3801. virtual const char *queryJobName() const
  3802. { return c->queryJobName(); }
  3803. virtual IConstWUPlugin * getPluginByName(const char * name) const
  3804. { return c->getPluginByName(name); }
  3805. virtual IConstWUPluginIterator & getPlugins() const
  3806. { return c->getPlugins(); }
  3807. virtual IConstWULibrary* getLibraryByName(const char *name) const
  3808. { return c->getLibraryByName(name); }
  3809. virtual IConstWULibraryIterator & getLibraries() const
  3810. { return c->getLibraries(); }
  3811. virtual WUPriorityClass getPriority() const
  3812. { return c->getPriority(); }
  3813. virtual const char *queryPriorityDesc() const
  3814. { return c->queryPriorityDesc(); }
  3815. virtual int getPriorityLevel() const
  3816. { return c->getPriorityLevel(); }
  3817. virtual int getPriorityValue() const
  3818. { return c->getPriorityValue(); }
  3819. virtual IConstWUQuery * getQuery() const
  3820. { return c->getQuery(); }
  3821. virtual IConstWUWebServicesInfo * getWebServicesInfo() const
  3822. { return c->getWebServicesInfo(); }
  3823. virtual bool getRescheduleFlag() const
  3824. { return c->getRescheduleFlag(); }
  3825. virtual IConstWUResult * getResultByName(const char * name) const
  3826. { return c->getResultByName(name); }
  3827. virtual IConstWUResult * getResultBySequence(unsigned seq) const
  3828. { return c->getResultBySequence(seq); }
  3829. virtual IConstWUResult * getQueryResultByName(const char * name) const
  3830. { return c->getQueryResultByName(name); }
  3831. virtual unsigned getResultLimit() const
  3832. { return c->getResultLimit(); }
  3833. virtual IConstWUResultIterator & getResults() const
  3834. { return c->getResults(); }
  3835. virtual IStringVal & getScope(IStringVal & str) const
  3836. { return c->getScope(str); }
  3837. virtual IStringVal & getWorkunitDistributedAccessToken(IStringVal & str) const
  3838. { return c->getWorkunitDistributedAccessToken(str); }
  3839. virtual WUState getState() const
  3840. { return c->getState(); }
  3841. virtual IStringVal & getStateEx(IStringVal & str) const
  3842. { return c->getStateEx(str); }
  3843. virtual __int64 getAgentSession() const
  3844. { return c->getAgentSession(); }
  3845. virtual unsigned getAgentPID() const
  3846. { return c->getAgentPID(); }
  3847. virtual const char *queryStateDesc() const
  3848. { return c->queryStateDesc(); }
  3849. virtual bool getRunningGraph(IStringVal & graphName, WUGraphIDType & subId) const
  3850. { return c->getRunningGraph(graphName, subId); }
  3851. virtual IConstWUScopeIterator & getScopeIterator(const WuScopeFilter & filter) const override
  3852. { return c->getScopeIterator(filter); }
  3853. virtual bool getStatistic(stat_type & value, const char * scope, StatisticKind kind) const override
  3854. { return c->getStatistic(value, scope, kind); }
  3855. virtual IStringVal & getSnapshot(IStringVal & str) const
  3856. { return c->getSnapshot(str); }
  3857. virtual const char *queryUser() const
  3858. { return c->queryUser(); }
  3859. virtual ErrorSeverity getWarningSeverity(unsigned code, ErrorSeverity defaultSeverity) const
  3860. { return c->getWarningSeverity(code, defaultSeverity); }
  3861. virtual const char *queryWuScope() const
  3862. { return c->queryWuScope(); }
  3863. virtual const char *queryWuid() const
  3864. { return c->queryWuid(); }
  3865. virtual IConstWUResult * getGlobalByName(const char * name) const
  3866. { return c->getGlobalByName(name); }
  3867. virtual IConstWUResult * getTemporaryByName(const char * name) const
  3868. { return c->getTemporaryByName(name); }
  3869. virtual IConstWUResultIterator & getTemporaries() const
  3870. { return c->getTemporaries(); }
  3871. virtual IConstWUResult * getVariableByName(const char * name) const
  3872. { return c->getVariableByName(name); }
  3873. virtual IConstWUResultIterator & getVariables() const
  3874. { return c->getVariables(); }
  3875. virtual bool isProtected() const
  3876. { return c->isProtected(); }
  3877. virtual bool isPausing() const
  3878. { return c->isPausing(); }
  3879. virtual IWorkUnit & lock()
  3880. { ((CInterface *)this)->Link(); return (IWorkUnit &) *this; }
  3881. virtual bool reload()
  3882. { UNIMPLEMENTED; }
  3883. virtual void subscribe(WUSubscribeOptions options)
  3884. { c->subscribe(options); }
  3885. virtual void requestAbort()
  3886. { c->requestAbort(); }
  3887. virtual unsigned calculateHash(unsigned prevHash)
  3888. { return queryExtendedWU(c)->calculateHash(prevHash); }
  3889. virtual void copyWorkUnit(IConstWorkUnit *cached, bool copyStats, bool all)
  3890. { queryExtendedWU(c)->copyWorkUnit(cached, copyStats, all); }
  3891. virtual IPropertyTree *queryPTree() const
  3892. { return queryExtendedWU(c)->queryPTree(); }
  3893. virtual IPropertyTree *getUnpackedTree(bool includeProgress) const
  3894. { return queryExtendedWU(c)->getUnpackedTree(includeProgress); }
  3895. virtual bool archiveWorkUnit(const char *base,bool del,bool deldll,bool deleteOwned,bool exportAssociatedFiles)
  3896. { return queryExtendedWU(c)->archiveWorkUnit(base,del,deldll,deleteOwned,exportAssociatedFiles); }
  3897. virtual unsigned queryFileUsage(const char *filename) const
  3898. { return c->queryFileUsage(filename); }
  3899. virtual IConstWUFileUsageIterator * getFieldUsage() const
  3900. { return c->getFieldUsage(); }
  3901. virtual bool getFieldUsageArray(StringArray & filenames, StringArray & columnnames, const char * clusterName) const
  3902. { return c->getFieldUsageArray(filenames, columnnames, clusterName); }
  3903. virtual IJlibDateTime & getTimeScheduled(IJlibDateTime &val) const
  3904. { return c->getTimeScheduled(val); }
  3905. virtual unsigned getDebugAgentListenerPort() const
  3906. { return c->getDebugAgentListenerPort(); }
  3907. virtual IStringVal & getDebugAgentListenerIP(IStringVal &ip) const
  3908. { return c->getDebugAgentListenerIP(ip); }
  3909. virtual IStringVal & getXmlParams(IStringVal & params, bool hidePasswords) const
  3910. { return c->getXmlParams(params, hidePasswords); }
  3911. virtual const IPropertyTree *getXmlParams() const
  3912. { return c->getXmlParams(); }
  3913. virtual unsigned __int64 getHash() const
  3914. { return c->getHash(); }
  3915. virtual IStringIterator *getLogs(const char *type, const char *instance) const
  3916. { return c->getLogs(type, instance); }
  3917. virtual IStringIterator *getProcesses(const char *type) const
  3918. { return c->getProcesses(type); }
  3919. virtual IPropertyTreeIterator* getProcesses(const char *type, const char *instance) const
  3920. { return c->getProcesses(type, instance); }
  3921. virtual unsigned getTotalThorTime() const
  3922. { return c->getTotalThorTime(); }
  3923. virtual WUGraphState queryGraphState(const char *graphName) const
  3924. { return c->queryGraphState(graphName); }
  3925. virtual WUGraphState queryNodeState(const char *graphName, WUGraphIDType nodeId) const
  3926. { return c->queryNodeState(graphName, nodeId); }
  3927. virtual void setGraphState(const char *graphName, unsigned wfid, WUGraphState state) const
  3928. { c->setGraphState(graphName, wfid, state); }
  3929. virtual void setNodeState(const char *graphName, WUGraphIDType nodeId, WUGraphState state) const
  3930. { c->setNodeState(graphName, nodeId, state); }
  3931. virtual IWUGraphStats *updateStats(const char *graphName, StatisticCreatorType creatorType, const char * creator, unsigned _wfid, unsigned subgraph, bool merge) const override
  3932. { return c->updateStats(graphName, creatorType, creator, _wfid, subgraph, merge); }
  3933. virtual void clearGraphProgress() const
  3934. { c->clearGraphProgress(); }
  3935. virtual IStringVal & getAbortBy(IStringVal & str) const
  3936. { return c->getAbortBy(str); }
  3937. virtual unsigned __int64 getAbortTimeStamp() const
  3938. { return c->getAbortTimeStamp(); }
  3939. virtual cost_type getExecuteCost() const
  3940. { return c->getExecuteCost(); }
  3941. virtual cost_type getFileAccessCost() const
  3942. { return c->getFileAccessCost(); }
  3943. virtual void import(IPropertyTree *wuTree, IPropertyTree *graphProgressTree)
  3944. { return c->import(wuTree, graphProgressTree); }
  3945. virtual void clearExceptions(const char *source=nullptr)
  3946. { c->clearExceptions(source); }
  3947. virtual void commit()
  3948. { c->commit(); }
  3949. virtual IWUException * createException()
  3950. { return c->createException(); }
  3951. virtual void addProcess(const char *type, const char *instance, unsigned pid, unsigned max, const char *pattern, bool singleLog, const char *log)
  3952. { c->addProcess(type, instance, pid, max, pattern, singleLog, log); }
  3953. virtual void protect(bool protectMode)
  3954. { c->protect(protectMode); }
  3955. virtual void setAction(WUAction action)
  3956. { c->setAction(action); }
  3957. virtual void setApplicationValue(const char * application, const char * propname, const char * value, bool overwrite)
  3958. { c->setApplicationValue(application, propname, value, overwrite); }
  3959. virtual void setApplicationValueInt(const char * application, const char * propname, int value, bool overwrite)
  3960. { c->setApplicationValueInt(application, propname, value, overwrite); }
  3961. virtual void incEventScheduledCount()
  3962. { c->incEventScheduledCount(); }
  3963. virtual void setIsQueryService(bool value)
  3964. { c->setIsQueryService(value); }
  3965. virtual void setCloneable(bool value)
  3966. { c->setCloneable(value); }
  3967. virtual void setIsClone(bool value)
  3968. { c->setIsClone(value); }
  3969. virtual void setClusterName(const char * value)
  3970. { c->setClusterName(value); }
  3971. virtual void setCodeVersion(unsigned version, const char * buildVersion, const char * eclVersion)
  3972. { c->setCodeVersion(version, buildVersion, eclVersion); }
  3973. virtual void setDebugValue(const char * propname, const char * value, bool overwrite)
  3974. { c->setDebugValue(propname, value, overwrite); }
  3975. virtual void setDebugValueInt(const char * propname, int value, bool overwrite)
  3976. { c->setDebugValueInt(propname, value, overwrite); }
  3977. virtual void setJobName(const char * value)
  3978. { c->setJobName(value); }
  3979. virtual void setPriority(WUPriorityClass cls)
  3980. { c->setPriority(cls); }
  3981. virtual void setPriorityLevel(int level)
  3982. { c->setPriorityLevel(level); }
  3983. virtual void setRescheduleFlag(bool value)
  3984. { c->setRescheduleFlag(value); }
  3985. virtual void setResultLimit(unsigned value)
  3986. { c->setResultLimit(value); }
  3987. virtual void setState(WUState state)
  3988. { c->setState(state); }
  3989. virtual void setStateEx(const char * text)
  3990. { c->setStateEx(text); }
  3991. virtual void setAgentSession(__int64 sessionId)
  3992. { c->setAgentSession(sessionId); }
  3993. virtual void setStatistic(StatisticCreatorType creatorType, const char * creator, StatisticScopeType scopeType, const char * scope, StatisticKind kind, const char * optDescription, unsigned __int64 value, unsigned __int64 count, unsigned __int64 maxValue, StatsMergeAction mergeAction)
  3994. { c->setStatistic(creatorType, creator, scopeType, scope, kind, optDescription, value, count, maxValue, mergeAction); }
  3995. virtual void setTracingValue(const char * propname, const char * value)
  3996. { c->setTracingValue(propname, value); }
  3997. virtual void setTracingValueInt(const char * propname, int value)
  3998. { c->setTracingValueInt(propname, value); }
  3999. virtual void setTracingValueInt64(const char * propname, __int64 value)
  4000. { c->setTracingValueInt64(propname, value); }
  4001. virtual void setUser(const char * value)
  4002. { c->setUser(value); }
  4003. virtual void setWuScope(const char * value)
  4004. { c->setWuScope(value); }
  4005. virtual IWorkflowItem* addWorkflowItem(unsigned wfid, WFType type, WFMode mode, unsigned success, unsigned failure, unsigned recovery, unsigned retriesAllowed, unsigned contingencyFor)
  4006. { return c->addWorkflowItem(wfid, type, mode, success, failure, recovery, retriesAllowed, contingencyFor); }
  4007. virtual void syncRuntimeWorkflow(IWorkflowItemArray * array)
  4008. { c->syncRuntimeWorkflow(array); }
  4009. virtual IWorkflowItemIterator * updateWorkflowItems()
  4010. { return c->updateWorkflowItems(); }
  4011. virtual void resetWorkflow()
  4012. { c->resetWorkflow(); }
  4013. virtual void schedule()
  4014. { c->schedule(); }
  4015. virtual void deschedule()
  4016. { c->deschedule(); }
  4017. virtual unsigned addLocalFileUpload(LocalFileUploadType type, char const * source, char const * destination, char const * eventTag)
  4018. { return c->addLocalFileUpload(type, source, destination, eventTag); }
  4019. virtual IWUResult * updateGlobalByName(const char * name)
  4020. { return c->updateGlobalByName(name); }
  4021. virtual void createGraph(const char * name, const char *label, WUGraphType type, IPropertyTree *xgmml, unsigned wfid)
  4022. { c->createGraph(name, label, type, xgmml, wfid); }
  4023. virtual IWUQuery * updateQuery()
  4024. { return c->updateQuery(); }
  4025. virtual IWUWebServicesInfo * updateWebServicesInfo(bool create)
  4026. { return c->updateWebServicesInfo(create); }
  4027. virtual IWUPlugin * updatePluginByName(const char * name)
  4028. { return c->updatePluginByName(name); }
  4029. virtual IWULibrary * updateLibraryByName(const char * name)
  4030. { return c->updateLibraryByName(name); }
  4031. virtual IWUResult * updateResultByName(const char * name)
  4032. { return c->updateResultByName(name); }
  4033. virtual IWUResult * updateResultBySequence(unsigned seq)
  4034. { return c->updateResultBySequence(seq); }
  4035. virtual IWUResult * updateTemporaryByName(const char * name)
  4036. { return c->updateTemporaryByName(name); }
  4037. virtual IWUResult * updateVariableByName(const char * name)
  4038. { return c->updateVariableByName(name); }
  4039. virtual void addFile(const char *fileName, StringArray *clusters, unsigned usageCount, WUFileKind fileKind, const char *graphOwner)
  4040. { c->addFile(fileName, clusters, usageCount, fileKind, graphOwner); }
  4041. virtual void noteFileRead(IDistributedFile *file)
  4042. { c->noteFileRead(file); }
  4043. virtual void noteFieldUsage(IPropertyTree * usage)
  4044. { c->noteFieldUsage(usage); }
  4045. virtual void releaseFile(const char *fileName)
  4046. { c->releaseFile(fileName); }
  4047. virtual void resetBeforeGeneration()
  4048. { c->resetBeforeGeneration(); }
  4049. virtual void deleteTempFiles(const char *graph, bool deleteOwned, bool deleteJobOwned)
  4050. { c->deleteTempFiles(graph, deleteOwned, deleteJobOwned); }
  4051. virtual void deleteTemporaries()
  4052. { c->deleteTemporaries(); }
  4053. virtual void addDiskUsageStats(__int64 avgNodeUsage, unsigned minNode, __int64 minNodeUsage, unsigned maxNode, __int64 maxNodeUsage, __int64 graphId)
  4054. { c->addDiskUsageStats(avgNodeUsage, minNode, minNodeUsage, maxNode, maxNodeUsage, graphId); }
  4055. virtual IPropertyTree * getDiskUsageStats()
  4056. { return c->getDiskUsageStats(); }
  4057. virtual IPropertyTreeIterator & getFileIterator() const
  4058. { return c->getFileIterator(); }
  4059. virtual IPropertyTreeIterator & getFilesReadIterator() const
  4060. { return c->getFilesReadIterator(); }
  4061. virtual void setSnapshot(const char * value)
  4062. { c->setSnapshot(value); }
  4063. virtual void setWarningSeverity(unsigned code, ErrorSeverity severity)
  4064. { c->setWarningSeverity(code, severity); }
  4065. virtual void setTimeScheduled(const IJlibDateTime &val)
  4066. { c->setTimeScheduled(val); }
  4067. virtual void setDebugAgentListenerPort(unsigned port)
  4068. { c->setDebugAgentListenerPort(port); }
  4069. virtual void setDebugAgentListenerIP(const char * ip)
  4070. { c->setDebugAgentListenerIP(ip); }
  4071. virtual void setXmlParams(const char *params)
  4072. { c->setXmlParams(params); }
  4073. virtual void setXmlParams(IPropertyTree *tree)
  4074. { c->setXmlParams(tree); }
  4075. virtual void setHash(unsigned __int64 hash)
  4076. { c->setHash(hash); }
  4077. // ILocalWorkUnit - used for debugging etc
  4078. virtual void serialize(MemoryBuffer &tgt)
  4079. { c->serialize(tgt); }
  4080. virtual void deserialize(MemoryBuffer &src)
  4081. { c->deserialize(src); }
  4082. virtual bool switchThorQueue(const char *cluster, IQueueSwitcher *qs)
  4083. { return c->switchThorQueue(cluster,qs); }
  4084. virtual void setAllowedClusters(const char *value)
  4085. { c->setAllowedClusters(value); }
  4086. virtual IStringVal& getAllowedClusters(IStringVal &str) const
  4087. { return c->getAllowedClusters(str); }
  4088. virtual void remoteCheckAccess(IUserDescriptor *user, bool writeaccess) const
  4089. { c->remoteCheckAccess(user,writeaccess); }
  4090. virtual void setAllowAutoQueueSwitch(bool val)
  4091. { c->setAllowAutoQueueSwitch(val); }
  4092. virtual bool getAllowAutoQueueSwitch() const
  4093. { return c->getAllowAutoQueueSwitch(); }
  4094. virtual void setLibraryInformation(const char * name, unsigned interfaceHash, unsigned definitionHash)
  4095. { c->setLibraryInformation(name, interfaceHash, definitionHash); }
  4096. virtual void setResultInt(const char * name, unsigned sequence, __int64 val)
  4097. { c->setResultInt(name, sequence, val); }
  4098. virtual void setResultUInt(const char * name, unsigned sequence, unsigned __int64 val)
  4099. { c->setResultUInt(name, sequence, val); }
  4100. virtual void setResultReal(const char *name, unsigned sequence, double val)
  4101. { c->setResultReal(name, sequence, val); }
  4102. virtual void setResultVarString(const char * stepname, unsigned sequence, const char *val)
  4103. { c->setResultVarString(stepname, sequence, val); }
  4104. virtual void setResultVarUnicode(const char * stepname, unsigned sequence, UChar const *val)
  4105. { c->setResultVarUnicode(stepname, sequence, val); }
  4106. virtual void setResultString(const char * stepname, unsigned sequence, int len, const char *val)
  4107. { c->setResultString(stepname, sequence, len, val); }
  4108. virtual void setResultData(const char * stepname, unsigned sequence, int len, const void *val)
  4109. { c->setResultData(stepname, sequence, len, val); }
  4110. virtual void setResultRaw(const char * name, unsigned sequence, int len, const void *val)
  4111. { c->setResultRaw(name, sequence, len, val); }
  4112. virtual void setResultSet(const char * name, unsigned sequence, bool isAll, size32_t len, const void *val, ISetToXmlTransformer *xform)
  4113. { c->setResultSet(name, sequence, isAll, len, val, xform); }
  4114. virtual void setResultUnicode(const char * name, unsigned sequence, int len, UChar const * val)
  4115. { c->setResultUnicode(name, sequence, len, val); }
  4116. virtual void setResultBool(const char *name, unsigned sequence, bool val)
  4117. { c->setResultBool(name, sequence, val); }
  4118. virtual void setResultDecimal(const char *name, unsigned sequence, int len, int precision, bool isSigned, const void *val)
  4119. { c->setResultDecimal(name, sequence, len, precision, isSigned, val); }
  4120. virtual void setResultDataset(const char * name, unsigned sequence, size32_t len, const void *val, unsigned numRows, bool extend)
  4121. { c->setResultDataset(name, sequence, len, val, numRows, extend); }
  4122. };
  4123. IPropertyTree &CDaliWuGraphStats::queryProgressTree()
  4124. {
  4125. conn.setown(owner->getWritableProgressConnection(graphName, wfid));
  4126. return *conn->queryRoot();
  4127. }
  4128. class CLocalWUAssociated : implements IConstWUAssociatedFile, public CInterface
  4129. {
  4130. Owned<IPropertyTree> p;
  4131. public:
  4132. IMPLEMENT_IINTERFACE;
  4133. CLocalWUAssociated(IPropertyTree *p);
  4134. virtual WUFileType getType() const;
  4135. virtual IStringVal & getDescription(IStringVal & ret) const;
  4136. virtual IStringVal & getIp(IStringVal & ret) const;
  4137. virtual IStringVal & getName(IStringVal & ret) const;
  4138. virtual IStringVal & getNameTail(IStringVal & ret) const;
  4139. virtual unsigned getCrc() const;
  4140. virtual unsigned getMinActivityId() const;
  4141. virtual unsigned getMaxActivityId() const;
  4142. };
  4143. class CLocalWUQuery : implements IWUQuery, public CInterface
  4144. {
  4145. Owned<IPropertyTree> p;
  4146. mutable IArrayOf<IConstWUAssociatedFile> associated;
  4147. mutable CriticalSection crit;
  4148. mutable bool associatedCached;
  4149. private:
  4150. void addSpecialCaseAssociated(WUFileType type, const char * propname, unsigned crc) const;
  4151. void loadAssociated() const;
  4152. public:
  4153. IMPLEMENT_IINTERFACE;
  4154. CLocalWUQuery(IPropertyTree *p);
  4155. virtual WUQueryType getQueryType() const;
  4156. virtual IStringVal& getQueryText(IStringVal &str) const;
  4157. virtual IStringVal& getQueryShortText(IStringVal &str) const;
  4158. virtual IStringVal& getQueryName(IStringVal &str) const;
  4159. virtual IStringVal & getQueryMainDefinition(IStringVal & str) const;
  4160. virtual IStringVal& getQueryDllName(IStringVal &str) const;
  4161. virtual unsigned getQueryDllCrc() const;
  4162. virtual IStringVal& getQueryCppName(IStringVal &str) const;
  4163. virtual IStringVal& getQueryResTxtName(IStringVal &str) const;
  4164. virtual IConstWUAssociatedFile * getAssociatedFile(WUFileType type, unsigned index) const;
  4165. virtual IConstWUAssociatedFileIterator& getAssociatedFiles() const;
  4166. virtual bool isArchive() const;
  4167. virtual bool hasArchive() const
  4168. {
  4169. return p->getPropBool("@hasArchive");
  4170. }
  4171. virtual void setQueryType(WUQueryType qt);
  4172. virtual void setQueryText(const char *pstr);
  4173. virtual void setQueryName(const char *);
  4174. virtual void setQueryMainDefinition(const char * str);
  4175. virtual void addAssociatedFile(WUFileType type, const char * name, const char * ip, const char * desc, unsigned crc, unsigned minActivity, unsigned maxActivity);
  4176. virtual void removeAssociatedFiles();
  4177. virtual void removeAssociatedFile(WUFileType type, const char * name, const char * desc);
  4178. };
  4179. class CLocalWUWebServicesInfo : implements IWUWebServicesInfo, public CInterface
  4180. {
  4181. Owned<IPropertyTree> p;
  4182. mutable CriticalSection crit;
  4183. private:
  4184. public:
  4185. IMPLEMENT_IINTERFACE;
  4186. CLocalWUWebServicesInfo(IPropertyTree *p);
  4187. virtual IStringVal& getModuleName(IStringVal &str) const;
  4188. virtual IStringVal& getAttributeName(IStringVal &str) const;
  4189. virtual IStringVal& getDefaultName(IStringVal &str) const;
  4190. virtual IStringVal& getInfo(const char *name, IStringVal &str) const;
  4191. virtual IStringVal& getText(const char *name, IStringVal &str) const;
  4192. virtual unsigned getWebServicesCRC() const;
  4193. virtual void setModuleName(const char *);
  4194. virtual void setAttributeName(const char *);
  4195. virtual void setDefaultName(const char *);
  4196. virtual void setInfo(const char *name, const char *info);
  4197. virtual void setText(const char *name, const char *info);
  4198. virtual void setWebServicesCRC(unsigned);
  4199. };
  4200. class CLocalWUResult : implements IWUResult, public CInterface
  4201. {
  4202. friend class CLocalWorkUnit;
  4203. mutable CriticalSection crit;
  4204. Owned<IPropertyTree> p;
  4205. Owned<IProperties> xmlns;
  4206. public:
  4207. IMPLEMENT_IINTERFACE;
  4208. CLocalWUResult(IPropertyTree *props);
  4209. ~CLocalWUResult() { try { p.clear(); } catch (IException *E) {E->Release();}}
  4210. virtual WUResultStatus getResultStatus() const;
  4211. virtual IStringVal& getResultName(IStringVal &str) const;
  4212. virtual int getResultSequence() const;
  4213. virtual bool isResultScalar() const;
  4214. virtual IStringVal& getResultXml(IStringVal &str, bool hidePasswords) const;
  4215. virtual unsigned getResultFetchSize() const;
  4216. virtual __int64 getResultTotalRowCount() const;
  4217. virtual __int64 getResultRowCount() const;
  4218. virtual void getResultDataset(IStringVal & ecl, IStringVal & defs) const;
  4219. virtual IStringVal& getResultLogicalName(IStringVal &ecl) const;
  4220. virtual IStringVal& getResultKeyField(IStringVal& ecl) const;
  4221. virtual unsigned getResultRequestedRows() const;
  4222. virtual __int64 getResultInt() const;
  4223. virtual bool getResultBool() const;
  4224. virtual double getResultReal() const;
  4225. virtual IStringVal& getResultString(IStringVal & str, bool hidePassword) const;
  4226. virtual IDataVal& getResultRaw(IDataVal & data, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const;
  4227. virtual IDataVal& getResultUnicode(IDataVal & data) const;
  4228. virtual void getResultDecimal(void * val, unsigned length, unsigned precision, bool isSigned) const;
  4229. virtual IStringVal& getResultEclSchema(IStringVal & str) const;
  4230. virtual __int64 getResultRawSize(IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const;
  4231. virtual IDataVal& getResultRaw(IDataVal & data, __int64 from, __int64 length, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const;
  4232. virtual IStringVal& getResultRecordSizeEntry(IStringVal & str) const;
  4233. virtual IStringVal& getResultTransformerEntry(IStringVal & str) const;
  4234. virtual __int64 getResultRowLimit() const;
  4235. virtual IStringVal& getResultFilename(IStringVal & str) const;
  4236. virtual WUResultFormat getResultFormat() const;
  4237. virtual unsigned getResultHash() const;
  4238. virtual bool getResultIsAll() const;
  4239. virtual IProperties *queryResultXmlns();
  4240. virtual IStringVal& getResultFieldOpt(const char *name, IStringVal &str) const;
  4241. virtual void getSchema(IArrayOf<ITypeInfo> &types, StringAttrArray &names, IStringVal * ecl=NULL) const;
  4242. virtual void getResultWriteLocation(IStringVal & _graph, unsigned & _activityId) const;
  4243. // interface IWUResult
  4244. virtual void setResultStatus(WUResultStatus status);
  4245. virtual void setResultName(const char *name);
  4246. virtual void setResultSequence(unsigned seq);
  4247. virtual void setResultSchemaRaw(unsigned len, const void *schema);
  4248. virtual void setResultScalar(bool isScalar);
  4249. virtual void setResultRaw(unsigned len, const void *xml, WUResultFormat format);
  4250. virtual void setResultFetchSize(unsigned rows); // 0 means file-loaded
  4251. virtual void setResultTotalRowCount(__int64 rows); // -1 means unknown
  4252. virtual void setResultRowCount(__int64 rows);
  4253. virtual void setResultDataset(const char *ecl, const char *defs);
  4254. virtual void setResultLogicalName(const char *logicalName);
  4255. virtual void setResultKeyField(const char * name);
  4256. virtual void setResultRequestedRows(unsigned req);
  4257. virtual void setResultRecordSizeEntry(const char * val);
  4258. virtual void setResultTransformerEntry(const char * val);
  4259. virtual void setResultInt(__int64 val);
  4260. virtual void setResultReal(double val);
  4261. virtual void setResultBool(bool val);
  4262. virtual void setResultString(const char * val, unsigned length);
  4263. virtual void setResultUnicode(const void * val, unsigned length);
  4264. virtual void setResultData(const void * val, unsigned length);
  4265. virtual void setResultDecimal(const void * val, unsigned length);
  4266. virtual void addResultRaw(unsigned len, const void * data, WUResultFormat format);
  4267. virtual void setResultRowLimit(__int64 value);
  4268. virtual void setResultFilename(const char * name);
  4269. virtual void setResultUInt(unsigned __int64 val);
  4270. virtual void setResultIsAll(bool value);
  4271. virtual void setResultFormat(WUResultFormat format);
  4272. virtual void setResultXML(const char *val);
  4273. virtual void setResultRow(unsigned len, const void * data);
  4274. virtual void setResultXmlns(const char *prefix, const char *uri);
  4275. virtual void setResultFieldOpt(const char *name, const char *value);
  4276. virtual void setResultWriteLocation(const char * _graph, unsigned _activityId);
  4277. virtual IPropertyTree *queryPTree() { return p; }
  4278. };
  4279. class CLocalWUPlugin : implements IWUPlugin, public CInterface
  4280. {
  4281. Owned<IPropertyTree> p;
  4282. public:
  4283. IMPLEMENT_IINTERFACE;
  4284. CLocalWUPlugin(IPropertyTree *p);
  4285. virtual IStringVal& getPluginName(IStringVal &str) const;
  4286. virtual IStringVal& getPluginVersion(IStringVal &str) const;
  4287. virtual void setPluginName(const char *str);
  4288. virtual void setPluginVersion(const char *str);
  4289. };
  4290. class CLocalWULibrary : implements IWULibrary, public CInterface
  4291. {
  4292. Owned<IPropertyTree> p;
  4293. public:
  4294. IMPLEMENT_IINTERFACE;
  4295. CLocalWULibrary(IPropertyTree *p);
  4296. virtual IStringVal & getName(IStringVal & str) const;
  4297. virtual void setName(const char * str);
  4298. };
  4299. class CLocalWUException : implements IWUException, public CInterface
  4300. {
  4301. Owned<IPropertyTree> p;
  4302. public:
  4303. IMPLEMENT_IINTERFACE;
  4304. CLocalWUException(IPropertyTree *p);
  4305. virtual IStringVal& getExceptionSource(IStringVal &str) const override;
  4306. virtual IStringVal& getExceptionMessage(IStringVal &str) const override;
  4307. virtual unsigned getExceptionCode() const override;
  4308. virtual ErrorSeverity getSeverity() const override;
  4309. virtual IStringVal & getTimeStamp(IStringVal & dt) const override;
  4310. virtual IStringVal & getExceptionFileName(IStringVal & str) const override;
  4311. virtual unsigned getExceptionLineNo() const override;
  4312. virtual unsigned getExceptionColumn() const override;
  4313. virtual unsigned getActivityId() const override;
  4314. virtual unsigned getSequence() const override;
  4315. virtual const char * queryScope() const override;
  4316. virtual unsigned getPriority() const override;
  4317. virtual void setExceptionSource(const char *str) override;
  4318. virtual void setExceptionMessage(const char *str) override;
  4319. virtual void setExceptionCode(unsigned code) override;
  4320. virtual void setSeverity(ErrorSeverity level) override;
  4321. virtual void setTimeStamp(const char * dt) override;
  4322. virtual void setExceptionFileName(const char *str) override;
  4323. virtual void setExceptionLineNo(unsigned r) override;
  4324. virtual void setExceptionColumn(unsigned c) override;
  4325. virtual void setActivityId(unsigned _id) override;
  4326. virtual void setScope(const char * _scope) override;
  4327. virtual void setPriority(unsigned _priority) override;
  4328. };
  4329. //==========================================================================================
  4330. extern WORKUNIT_API bool isSpecialResultSequence(unsigned sequence)
  4331. {
  4332. switch ((int) sequence)
  4333. {
  4334. case ResultSequenceInternal:
  4335. case ResultSequenceOnce:
  4336. case ResultSequencePersist:
  4337. case ResultSequenceStored:
  4338. return true;
  4339. default:
  4340. assertex(sequence <= INT_MAX);
  4341. if ((int) sequence >= LibraryBaseSequence)
  4342. return true;
  4343. return false;
  4344. }
  4345. }
  4346. class CConstWUArrayIterator : implements IConstWorkUnitIterator, public CInterface
  4347. {
  4348. unsigned curTreeNum;
  4349. IArrayOf<IPropertyTree> trees;
  4350. Owned<IConstWorkUnitInfo> cur;
  4351. void setCurrent()
  4352. {
  4353. cur.setown(new CLightweightWorkunitInfo(trees.item(curTreeNum)));
  4354. }
  4355. public:
  4356. IMPLEMENT_IINTERFACE;
  4357. CConstWUArrayIterator(IArrayOf<IPropertyTree> &_trees)
  4358. {
  4359. ForEachItemIn(t, _trees)
  4360. trees.append(*LINK(&_trees.item(t)));
  4361. curTreeNum = 0;
  4362. }
  4363. bool first()
  4364. {
  4365. curTreeNum = 0;
  4366. return next();
  4367. }
  4368. bool isValid()
  4369. {
  4370. return (NULL != cur.get());
  4371. }
  4372. bool next()
  4373. {
  4374. if (curTreeNum >= trees.ordinality())
  4375. {
  4376. cur.clear();
  4377. return false;
  4378. }
  4379. setCurrent();
  4380. ++curTreeNum;
  4381. return true;
  4382. }
  4383. IConstWorkUnitInfo & query() { return *cur; }
  4384. };
  4385. class CLocalWUFieldUsage : public CInterface, implements IConstWUFieldUsage
  4386. {
  4387. Owned<IPropertyTree> p;
  4388. public:
  4389. IMPLEMENT_IINTERFACE;
  4390. CLocalWUFieldUsage(IPropertyTree& _p) { p.setown(&_p); }
  4391. virtual const char * queryName() const { return p->queryProp("@name"); }
  4392. };
  4393. class CConstWUFieldUsageIterator : public CInterface, implements IConstWUFieldUsageIterator
  4394. {
  4395. public:
  4396. IMPLEMENT_IINTERFACE;
  4397. CConstWUFieldUsageIterator(IPropertyTreeIterator * tree) { iter.setown(tree); }
  4398. bool first() override { return iter->first(); }
  4399. bool isValid() override { return iter->isValid(); }
  4400. bool next() override { return iter->next(); }
  4401. IConstWUFieldUsage * get() const override { return new CLocalWUFieldUsage(iter->get()); }
  4402. private:
  4403. Owned<IPropertyTreeIterator> iter;
  4404. };
  4405. class CLocalWUFileUsage : public CInterface, implements IConstWUFileUsage
  4406. {
  4407. Owned<IPropertyTree> p;
  4408. public:
  4409. IMPLEMENT_IINTERFACE;
  4410. CLocalWUFileUsage(IPropertyTree& _p) { p.setown(&_p); }
  4411. virtual const char * queryName() const { return p->queryProp("@name"); }
  4412. virtual const char * queryType() const { return p->queryProp("@type"); }
  4413. virtual unsigned getNumFields() const { return p->getPropInt("@numFields"); }
  4414. virtual unsigned getNumFieldsUsed() const { return p->getPropInt("@numFieldsUsed"); }
  4415. virtual IConstWUFieldUsageIterator * getFields() const { return new CConstWUFieldUsageIterator(p->getElements("fields/field")); }
  4416. };
  4417. class CConstWUFileUsageIterator : public CInterface, implements IConstWUFileUsageIterator
  4418. {
  4419. public:
  4420. IMPLEMENT_IINTERFACE;
  4421. CConstWUFileUsageIterator(IPropertyTreeIterator * tree) { iter.setown(tree); }
  4422. bool first() override { return iter->first(); }
  4423. bool isValid() override { return iter->isValid(); }
  4424. bool next() override { return iter->next(); }
  4425. IConstWUFileUsage * get() const override { return new CLocalWUFileUsage(iter->get()); }
  4426. private:
  4427. Owned<IPropertyTreeIterator> iter;
  4428. };
  4429. //==========================================================================================
  4430. class CCachedJobNameIterator : implements IStringIterator, public CInterface
  4431. {
  4432. Owned<IPropertyTreeIterator> it;
  4433. public:
  4434. IMPLEMENT_IINTERFACE;
  4435. CCachedJobNameIterator(IPropertyTreeIterator *p) : it(p) {};
  4436. virtual bool first() { return it->first(); }
  4437. virtual bool next() { return it->next(); }
  4438. virtual bool isValid() { return it->isValid(); }
  4439. virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryName()+1); return s; }
  4440. };
  4441. EnumMapping workunitSortFields[] =
  4442. {
  4443. { WUSFuser, "@submitID" },
  4444. { WUSFcluster, "@clusterName" },
  4445. { WUSFjob, "@jobName" },
  4446. { WUSFstate, "@state" },
  4447. { WUSFpriority, "@priorityClass" },
  4448. { WUSFprotected, "@protected" },
  4449. { WUSFwuid, "@" },
  4450. { WUSFecl, "Query/ShortText" },
  4451. { WUSFfileread, "FilesRead/File/@name" },
  4452. { WUSFtotalthortime, "@totalThorTime" },
  4453. { WUSFwuidhigh, "@" },
  4454. { WUSFwildwuid, "@" },
  4455. { WUSFappvalue, "Application" },
  4456. { WUSFfilewritten, "Files/File/@name" },
  4457. { WUSFterm, NULL }
  4458. };
  4459. extern const char *queryFilterXPath(WUSortField field)
  4460. {
  4461. return getEnumText(field, workunitSortFields);
  4462. }
  4463. EnumMapping querySortFields[] =
  4464. {
  4465. { WUQSFId, "@id" },
  4466. { WUQSFwuid, "@wuid" },
  4467. { WUQSFname, "@name" },
  4468. { WUQSFdll, "@dll" },
  4469. { WUQSFmemoryLimit, "@memoryLimit" },
  4470. { WUQSFmemoryLimitHi, "@memoryLimit" },
  4471. { WUQSFtimeLimit, "@timeLimit" },
  4472. { WUQSFtimeLimitHi, "@timeLimit" },
  4473. { WUQSFwarnTimeLimit, "@warnTimeLimit" },
  4474. { WUQSFwarnTimeLimitHi, "@warnTimeLimit" },
  4475. { WUQSFpriority, "@priority" },
  4476. { WUQSFpriorityHi, "@priority" },
  4477. { WUQSFQuerySet, "@querySetId" },
  4478. { WUQSFActivited, "@activated" },
  4479. { WUQSFSuspendedByUser, "@suspended" },
  4480. { WUQSFLibrary, "Library"},
  4481. { WUQSFPublishedBy, "@publishedBy" },
  4482. { WUQSFterm, NULL }
  4483. };
  4484. class asyncRemoveDllWorkItem: public CInterface, implements IWorkQueueItem // class only used in asyncRemoveDll
  4485. {
  4486. StringAttr name;
  4487. public:
  4488. IMPLEMENT_IINTERFACE;
  4489. asyncRemoveDllWorkItem(const char * _name) : name(_name)
  4490. {
  4491. }
  4492. void execute()
  4493. {
  4494. PROGLOG("WU removeDll %s", name.get());
  4495. queryDllServer().removeDll(name, true, true); // <name>, removeDlls=true, removeDirectory=true
  4496. }
  4497. };
  4498. class asyncRemoveRemoteFileWorkItem: public CInterface, implements IWorkQueueItem // class only used in asyncRemoveFile
  4499. {
  4500. RemoteFilename name;
  4501. public:
  4502. IMPLEMENT_IINTERFACE;
  4503. asyncRemoveRemoteFileWorkItem(const char * _ip, const char * _name)
  4504. {
  4505. SocketEndpoint ep(_ip);
  4506. name.setPath(ep, _name);
  4507. }
  4508. void execute()
  4509. {
  4510. Owned<IFile> file = createIFile(name);
  4511. PROGLOG("WU removeDll %s",file->queryFilename());
  4512. file->remove();
  4513. }
  4514. };
  4515. //==========================================================================================
  4516. class CConstQuerySetQueryIterator : implements IConstQuerySetQueryIterator, public CInterface
  4517. {
  4518. unsigned index;
  4519. IArrayOf<IPropertyTree> trees;
  4520. public:
  4521. IMPLEMENT_IINTERFACE;
  4522. CConstQuerySetQueryIterator(IArrayOf<IPropertyTree> &_trees)
  4523. {
  4524. ForEachItemIn(t, _trees)
  4525. trees.append(*LINK(&_trees.item(t)));
  4526. index = 0;
  4527. }
  4528. ~CConstQuerySetQueryIterator()
  4529. {
  4530. trees.kill();
  4531. }
  4532. bool first()
  4533. {
  4534. index = 0;
  4535. return (trees.ordinality()!=0);
  4536. }
  4537. bool next()
  4538. {
  4539. index++;
  4540. return (index<trees.ordinality());
  4541. }
  4542. bool isValid()
  4543. {
  4544. return (index<trees.ordinality());
  4545. }
  4546. IPropertyTree &query()
  4547. {
  4548. return trees.item(index);
  4549. }
  4550. };
  4551. class CSecurityCache
  4552. {
  4553. };
  4554. class CConstWUIterator : implements IConstWorkUnitIterator, public CInterface
  4555. {
  4556. public:
  4557. IMPLEMENT_IINTERFACE;
  4558. CConstWUIterator(IPropertyTreeIterator *_ptreeIter)
  4559. : ptreeIter(_ptreeIter)
  4560. {
  4561. }
  4562. bool first()
  4563. {
  4564. if (!ptreeIter->first())
  4565. {
  4566. cur.clear();
  4567. return false;
  4568. }
  4569. cur.setown(new CLightweightWorkunitInfo(ptreeIter->query()));
  4570. return true;
  4571. }
  4572. bool isValid()
  4573. {
  4574. return (NULL != cur.get());
  4575. }
  4576. bool next()
  4577. {
  4578. if (!ptreeIter->next())
  4579. {
  4580. cur.clear();
  4581. return false;
  4582. }
  4583. cur.setown(new CLightweightWorkunitInfo(ptreeIter->query()));
  4584. return true;
  4585. }
  4586. IConstWorkUnitInfo & query() { return *cur; }
  4587. private:
  4588. Owned<IConstWorkUnitInfo> cur;
  4589. Owned<IPropertyTreeIterator> ptreeIter;
  4590. };
  4591. class CSecureConstWUIterator : public CInterfaceOf<IConstWorkUnitIterator>
  4592. {
  4593. public:
  4594. CSecureConstWUIterator(IConstWorkUnitIterator *_parent, ISecManager *_secmgr=NULL, ISecUser *_secuser=NULL)
  4595. : parent(_parent), secmgr(_secmgr), secuser(_secuser)
  4596. {
  4597. assertex(_secuser && _secmgr);
  4598. }
  4599. bool first()
  4600. {
  4601. if (!parent->first())
  4602. return false;
  4603. return getNext();
  4604. }
  4605. bool next()
  4606. {
  4607. if (!parent->next())
  4608. return false;
  4609. return getNext();
  4610. }
  4611. virtual bool isValid()
  4612. {
  4613. return parent->isValid();
  4614. }
  4615. virtual IConstWorkUnitInfo &query()
  4616. {
  4617. return parent->query();
  4618. }
  4619. private:
  4620. Owned<IConstWorkUnitIterator> parent;
  4621. MapStringTo<int> scopePermissions;
  4622. Linked<ISecManager> secmgr;
  4623. Linked<ISecUser> secuser;
  4624. bool getNext() // scan for a workunit with permissions
  4625. {
  4626. do
  4627. {
  4628. const char *scopeName = parent->query().queryWuScope();
  4629. if (!scopeName || !*scopeName || checkScope(scopeName))
  4630. return true;
  4631. } while (parent->next());
  4632. return false;
  4633. }
  4634. bool checkScope(const char *scopeName)
  4635. {
  4636. int *perms = scopePermissions.getValue(scopeName);
  4637. SecAccessFlags perm;
  4638. if (!perms)
  4639. {
  4640. perm = secuser.get() ? secmgr->authorizeWorkunitScope(*secuser, scopeName) : SecAccess_Unavailable;
  4641. scopePermissions.setValue(scopeName, perm);
  4642. }
  4643. else
  4644. perm = (SecAccessFlags)*perms;
  4645. return perm >= SecAccess_Read;
  4646. }
  4647. };
  4648. CWorkUnitFactory::CWorkUnitFactory()
  4649. {
  4650. }
  4651. CWorkUnitFactory::~CWorkUnitFactory()
  4652. {
  4653. }
  4654. IWorkUnit* CWorkUnitFactory::createNamedWorkUnit(const char *wuid, const char *app, const char *scope, ISecManager *secmgr, ISecUser *secuser)
  4655. {
  4656. checkWuScopeSecAccess(scope, secmgr, secuser, SecAccess_Write, "Create", true, true);
  4657. Owned<CLocalWorkUnit> cw = _createWorkUnit(wuid, secmgr, secuser);
  4658. if (scope)
  4659. cw->setWuScope(scope); // Note - this may check access rights and throw exception. Is that correct? We might prefer to only check access once, and this will check on the lock too...
  4660. cw->setDistributedAccessToken(secuser ? secuser->getName() : "");//create and sign the workunit distributed access token
  4661. IWorkUnit* ret = &cw->lockRemote(false); // Note - this may throw exception if user does not have rights.
  4662. ret->setDebugValue("CREATED_BY", app, true);
  4663. ret->setDebugValue("CREATED_FOR", scope, true);
  4664. return ret;
  4665. }
  4666. IWorkUnit* CWorkUnitFactory::createWorkUnit(const char *app, const char *scope, ISecManager *secmgr, ISecUser *secuser)
  4667. {
  4668. StringBuffer wuid("W");
  4669. char result[32];
  4670. time_t ltime;
  4671. time( &ltime );
  4672. tm *today = localtime( &ltime ); // MORE - this is not threadsafe. But I probably don't care that much!
  4673. strftime(result, sizeof(result), "%Y%m%d-%H%M%S", today);
  4674. wuid.append(result);
  4675. if (workUnitTraceLevel > 1)
  4676. DBGLOG("createWorkUnit created %s", wuid.str());
  4677. IWorkUnit* ret = createNamedWorkUnit(wuid.str(), app, scope, secmgr, secuser);
  4678. if (workUnitTraceLevel > 1)
  4679. DBGLOG("createWorkUnit created %s", ret->queryWuid());
  4680. addTimeStamp(ret, SSTglobal, NULL, StWhenCreated);
  4681. return ret;
  4682. }
  4683. bool CWorkUnitFactory::deleteWorkUnitEx(const char * wuid, bool throwException, ISecManager *secmgr, ISecUser *secuser)
  4684. {
  4685. if (workUnitTraceLevel > 1)
  4686. DBGLOG("deleteWorkUnit %s", wuid);
  4687. StringBuffer wuRoot;
  4688. getXPath(wuRoot, wuid);
  4689. Owned<CLocalWorkUnit> cw = _updateWorkUnit(wuid, secmgr, secuser);
  4690. if (!checkWuSecAccess(*cw.get(), secmgr, secuser, SecAccess_Full, "delete", true, true))
  4691. return false;
  4692. if (throwException)
  4693. cw->cleanupAndDelete(true, true);
  4694. else
  4695. {
  4696. try
  4697. {
  4698. cw->cleanupAndDelete(true, true);
  4699. }
  4700. catch (IException *E)
  4701. {
  4702. StringBuffer s;
  4703. LOG(MCexception(E, MSGCLS_warning), E, s.append("Exception during deleteWorkUnit: ").append(wuid).str());
  4704. E->Release();
  4705. return false;
  4706. }
  4707. }
  4708. removeWorkUnitFromAllQueues(wuid); //known active workunits wouldn't make it this far
  4709. return true;
  4710. }
  4711. bool CWorkUnitFactory::deleteWorkUnit(const char * wuid, ISecManager *secmgr, ISecUser *secuser)
  4712. {
  4713. return deleteWorkUnitEx(wuid, false, secmgr, secuser);
  4714. }
  4715. IConstWorkUnit* CWorkUnitFactory::openWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  4716. {
  4717. StringBuffer wuidStr(wuid);
  4718. wuidStr.trim();
  4719. if (wuidStr.length() && ('w' == wuidStr.charAt(0)))
  4720. wuidStr.setCharAt(0, 'W');
  4721. if (!wuidStr.length() || ('W' != wuidStr.charAt(0)))
  4722. {
  4723. if (workUnitTraceLevel > 1)
  4724. DBGLOG("openWorkUnit %s invalid WUID", nullText(wuidStr.str()));
  4725. return NULL;
  4726. }
  4727. if (workUnitTraceLevel > 1)
  4728. DBGLOG("openWorkUnit %s", wuidStr.str());
  4729. Owned<IConstWorkUnit> wu = _openWorkUnit(wuid, secmgr, secuser);
  4730. if (wu)
  4731. {
  4732. if (!checkWuSecAccess(*wu, secmgr, secuser, SecAccess_Read, "opening", true, true))
  4733. return NULL; // Actually throws exception on failure, so won't reach here
  4734. return wu.getClear();
  4735. }
  4736. else
  4737. {
  4738. if (workUnitTraceLevel > 0)
  4739. IERRLOG("openWorkUnit %s not found", wuidStr.str());
  4740. return NULL;
  4741. }
  4742. }
  4743. IWorkUnit* CWorkUnitFactory::updateWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  4744. {
  4745. if (workUnitTraceLevel > 1)
  4746. DBGLOG("updateWorkUnit %s", wuid);
  4747. Owned<CLocalWorkUnit> wu = _updateWorkUnit(wuid, secmgr, secuser);
  4748. if (wu)
  4749. {
  4750. if (!checkWuSecAccess(*wu.get(), secmgr, secuser, SecAccess_Write, "updating", true, true))
  4751. return NULL;
  4752. return &wu->lockRemote(false);
  4753. }
  4754. else
  4755. {
  4756. if (workUnitTraceLevel > 0)
  4757. IERRLOG("updateWorkUnit %s not found", wuid);
  4758. return NULL;
  4759. }
  4760. }
  4761. IPropertyTree * pruneBranch(IPropertyTree * from, char const * xpath)
  4762. {
  4763. Owned<IPropertyTree> ret;
  4764. IPropertyTree * branch = from->queryPropTree(xpath);
  4765. if(branch)
  4766. {
  4767. ret.setown(createPTreeFromIPT(branch));
  4768. from->removeTree(branch);
  4769. }
  4770. return ret.getClear();
  4771. }
  4772. bool CWorkUnitFactory::restoreWorkUnit(const char *base, const char *wuid, bool restoreAssociated)
  4773. {
  4774. StringBuffer path(base);
  4775. addPathSepChar(path).append(wuid).append(".xml");
  4776. Owned<IPTree> pt = createPTreeFromXMLFile(path);
  4777. if (!pt)
  4778. return false;
  4779. CDateTime dt;
  4780. dt.setNow();
  4781. StringBuffer dts;
  4782. dt.getString(dts);
  4783. pt->setProp("@restoredDate", dts.str());
  4784. Owned<IPropertyTree> generatedDlls = pruneBranch(pt, "GeneratedDlls[1]");
  4785. Owned<IPropertyTree> associatedFiles;
  4786. IPropertyTree *srcAssociated = pt->queryPropTree("Query/Associated");
  4787. if (srcAssociated)
  4788. associatedFiles.setown(createPTreeFromIPT(srcAssociated));
  4789. // The updating of the repo is implementation specific...
  4790. if (!_restoreWorkUnit(pt.getClear(), wuid))
  4791. return false;
  4792. // now kludge back GeneratedDlls
  4793. if (generatedDlls)
  4794. {
  4795. Owned<IPropertyTreeIterator> dlls = generatedDlls->getElements("GeneratedDll");
  4796. for(dlls->first(); dlls->isValid(); dlls->next())
  4797. {
  4798. IPropertyTree & dll = dlls->query();
  4799. char const * name = dll.queryProp("@name");
  4800. char const * kind = dll.queryProp("@kind");
  4801. char const * location = dll.queryProp("@location");
  4802. Owned<IDllEntry> got = queryDllServer().getEntry(name);
  4803. if (!got)
  4804. {
  4805. RemoteFilename dstRfn;
  4806. dstRfn.setRemotePath(location);
  4807. StringBuffer srcPath(base);
  4808. addPathSepChar(srcPath);
  4809. dstRfn.getTail(srcPath);
  4810. OwnedIFile srcFile = createIFile(srcPath);
  4811. OwnedIFile dstFile = createIFile(dstRfn);
  4812. copyFile(dstFile, srcFile);
  4813. queryDllServer().registerDll(name, kind, location);
  4814. }
  4815. }
  4816. }
  4817. if (associatedFiles)
  4818. {
  4819. Owned<IPropertyTreeIterator> associated = associatedFiles->getElements("*");
  4820. ForEach(*associated)
  4821. {
  4822. IPropertyTree &file = associated->query();
  4823. const char *filename = file.queryProp("@filename");
  4824. SocketEndpoint ep(file.queryProp("@ip"));
  4825. RemoteFilename rfn;
  4826. rfn.setPath(ep, filename);
  4827. OwnedIFile dstFile = createIFile(rfn);
  4828. StringBuffer srcPath(base), name;
  4829. addPathSepChar(srcPath);
  4830. rfn.getTail(name);
  4831. srcPath.append(name);
  4832. if (generatedDlls)
  4833. {
  4834. VStringBuffer gDllPath("GeneratedDll[@name=\"%s\"]", name.str());
  4835. if (generatedDlls->hasProp(gDllPath))
  4836. continue; // generated dlls handled separately - see above
  4837. }
  4838. OwnedIFile srcFile = createIFile(srcPath);
  4839. if (srcFile->exists())
  4840. {
  4841. try
  4842. {
  4843. copyFile(dstFile, srcFile);
  4844. }
  4845. catch (IException *e)
  4846. {
  4847. VStringBuffer msg("Failed to restore associated file '%s' to destination '%s'", srcFile->queryFilename(), dstFile->queryFilename());
  4848. EXCLOG(e, msg.str());
  4849. e->Release();
  4850. }
  4851. }
  4852. }
  4853. }
  4854. return true;
  4855. }
  4856. void CWorkUnitFactory::importWorkUnit(const char *zapReportFileName, const char *zapReportPassword,
  4857. const char *importDir, const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
  4858. {
  4859. class CImportWorkUnitHelper
  4860. {
  4861. StringAttr zapReportFileName, zapReportPassword, importDir, user, wuid, fromWUID, scope;
  4862. StringBuffer unzipDir;
  4863. Owned<IPTree> wuTree, graphProgressTree;
  4864. bool findZAPFile(const char *mask, bool optional, StringBuffer &fileName)
  4865. {
  4866. Owned<IFile> d = createIFile(unzipDir);
  4867. if (!d->exists())
  4868. throw MakeStringException(WUERR_InvalidUserInput, "%s not found.", unzipDir.str());
  4869. Owned<IDirectoryIterator> di = d->directoryFiles(mask, false, false);
  4870. if (!di->first())
  4871. {
  4872. if (optional)
  4873. return false;
  4874. throw MakeStringException(WUERR_InvalidUserInput, "Failed to find %s in %s.", mask, unzipDir.str());
  4875. }
  4876. fileName.set(unzipDir).append(PATHSEPSTR);
  4877. di->getName(fileName);
  4878. if (di->next())
  4879. throw MakeStringException(WUERR_InvalidUserInput, "More than 1 %s files found in %s.", mask, unzipDir.str());
  4880. return true;
  4881. }
  4882. IPTree *readWUXMLFile(const char *pattern, bool optional)
  4883. {
  4884. StringBuffer fileName;
  4885. if (!findZAPFile(pattern, optional, fileName))
  4886. return nullptr;
  4887. Owned<IPTree> tree = createPTreeFromXMLFile(fileName);
  4888. if (!tree)
  4889. throw MakeStringException(WUERR_InvalidUserInput, "Failed to retrieve %s.", pattern);
  4890. Owned<IFile> f = createIFile(fileName);
  4891. f->remove();
  4892. return tree.getClear();
  4893. }
  4894. void updateWUQueryAssociatedFilesAttrs(const char *localIP)
  4895. {
  4896. ICopyArrayOf<IPropertyTree> fileTreesToRemove;
  4897. IPropertyTree *queryAssociatedTreeRoot = wuTree->queryPropTree("Query/Associated");
  4898. Owned<IPropertyTreeIterator> itr = queryAssociatedTreeRoot->getElements("File");
  4899. ForEach (*itr)
  4900. {
  4901. IPropertyTree &fileTree = itr->query();
  4902. const char *fileName = fileTree.queryProp("@filename");
  4903. StringBuffer fileNameWithPath(unzipDir);
  4904. fileNameWithPath.append(PATHSEPSTR).append(pathTail(fileName));
  4905. if (checkFileExists(fileNameWithPath)) //Check if the ZAP report contains this file.
  4906. {
  4907. fileTree.setProp("@ip", localIP);
  4908. fileTree.setProp("@filename", fileNameWithPath);
  4909. }
  4910. else
  4911. fileTreesToRemove.append(fileTree);
  4912. }
  4913. ForEachItemIn(r, fileTreesToRemove)
  4914. queryAssociatedTreeRoot->removeTree(&fileTreesToRemove.item(r));
  4915. }
  4916. void getWUAttributes(IWorkUnit *workunit)
  4917. {
  4918. wuid.set(workunit->queryWuid());
  4919. scope.set(workunit->queryWuScope());
  4920. }
  4921. IPTree *queryWUPTree() const
  4922. {
  4923. return wuTree;
  4924. }
  4925. IPTree *queryGraphProgressPTree() const
  4926. {
  4927. return graphProgressTree;
  4928. }
  4929. void setUNZIPDir()
  4930. { //Set a unique unzip folder inside the component's data folder
  4931. unzipDir.append(importDir).append(PATHSEPSTR).append(wuid.get());
  4932. }
  4933. void unzipZAPReport()
  4934. {
  4935. Owned<IFile> unzipFolder = createIFile(unzipDir);
  4936. if (!unzipFolder->exists())
  4937. unzipFolder->createDirectory();
  4938. else
  4939. {//This should not happen. Just in case.
  4940. throw MakeStringException(WUERR_CannotImportWorkunit, "Workunit %s had been created twice and passed to import()..", wuid.get());
  4941. }
  4942. //Unzip ZAP Report
  4943. VStringBuffer zipCommand("unzip %s -d %s", zapReportFileName.get(), unzipDir.str());
  4944. if (!isEmptyString(zapReportPassword))
  4945. zipCommand.appendf(" -P %s", zapReportPassword.get());
  4946. DWORD runcode;
  4947. bool success = invoke_program(zipCommand.str(), runcode, true);
  4948. if (!success)
  4949. throw MakeStringException(WUERR_CannotImportWorkunit, "Failed in execution : %s.", zipCommand.str());
  4950. if (runcode != 0)
  4951. throw MakeStringException(WUERR_CannotImportWorkunit, "Failed in execution : %s, error(%" I64F "i)", zipCommand.str(), (unsigned __int64) runcode);
  4952. }
  4953. void buildPTreesFromZAPReport()
  4954. {
  4955. wuTree.setown(readWUXMLFile("ZAPReport_W*.xml", false));
  4956. fromWUID.set(wuTree->queryName());
  4957. wuTree->setProp("@scope", scope);
  4958. //update QueryAssociatedFiles in WU XML;
  4959. updateWUQueryAssociatedFilesAttrs(GetCachedHostName());
  4960. graphProgressTree.setown(readWUXMLFile("ZAPReport_*.graphprogress", true));
  4961. }
  4962. void updateJobName(IWorkUnit *workunit)
  4963. {
  4964. StringBuffer newJobName;
  4965. const char *jobName = workunit->queryJobName();
  4966. if (isEmptyString(jobName))
  4967. newJobName.appendf("IMPORTED from %s", fromWUID.get());
  4968. else
  4969. newJobName.appendf("IMPORTED: %s", jobName);
  4970. workunit->setJobName(newJobName);
  4971. }
  4972. void setImportDebugAttribute(IWorkUnit *workunit)
  4973. {
  4974. StringBuffer attr, importDateTime;
  4975. CDateTime dt;
  4976. dt.setNow();
  4977. dt.getString(importDateTime);
  4978. attr.append("FromWUID=").append(fromWUID).append(",");
  4979. attr.append("ImportDT=").append(importDateTime).append(",");
  4980. attr.append("ZAPReport=").append(pathTail(zapReportFileName));
  4981. workunit->setDebugValue("imported", attr, true);
  4982. }
  4983. public:
  4984. CImportWorkUnitHelper(const char *_zapReportFileName, const char *_zapReportPassword, const char *_importDir, const char *_user)
  4985. : zapReportFileName(_zapReportFileName), zapReportPassword(_zapReportPassword), importDir(_importDir), user(_user) { };
  4986. void import(IWorkUnit *workunit)
  4987. {
  4988. getWUAttributes(workunit);
  4989. setUNZIPDir();
  4990. unzipZAPReport();
  4991. buildPTreesFromZAPReport();
  4992. workunit->import(queryWUPTree(), queryGraphProgressPTree());
  4993. setImportDebugAttribute(workunit);
  4994. updateJobName(workunit);
  4995. removeFileTraceIfFail(zapReportFileName);
  4996. }
  4997. };
  4998. Owned<IWorkUnit> newWU = createWorkUnit(app, user, secMgr, secUser);
  4999. CImportWorkUnitHelper helper(zapReportFileName, zapReportPassword, importDir, user);
  5000. helper.import(newWU);
  5001. }
  5002. int CWorkUnitFactory::setTracingLevel(int newLevel)
  5003. {
  5004. if (newLevel)
  5005. DBGLOG("Setting workunit trace level to %d", newLevel);
  5006. int level = workUnitTraceLevel;
  5007. workUnitTraceLevel = newLevel;
  5008. return level;
  5009. }
  5010. void CWorkUnitFactory::descheduleAllWorkUnits(ISecManager *secmgr, ISecUser *secuser)
  5011. {
  5012. Owned<IRemoteConnection> conn = querySDS().connect("/Schedule", myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  5013. if(!conn) return;
  5014. Owned<IPropertyTree> root(conn->queryRoot()->getBranch("."));
  5015. KeptAtomTable entries;
  5016. Owned<IPropertyTreeIterator> iter(root->getElements("*/*/*/*"));
  5017. StringBuffer wuid;
  5018. for(iter->first(); iter->isValid(); iter->next())
  5019. {
  5020. char const * entry = iter->query().queryName();
  5021. if(!entries.find(entry))
  5022. {
  5023. entries.addAtom(entry);
  5024. ncnameUnescape(entry, wuid.clear());
  5025. Owned<IWorkUnit> wu = updateWorkUnit(wuid, secmgr, secuser);
  5026. if(wu && (wu->getState() == WUStateWait))
  5027. wu->setState(WUStateCompleted);
  5028. }
  5029. }
  5030. bool more;
  5031. do more = root->removeProp("*"); while(more);
  5032. }
  5033. IConstQuerySetQueryIterator* CWorkUnitFactory::getQuerySetQueriesSorted( WUQuerySortField *sortorder, // list of fields to sort by (terminated by WUSFterm)
  5034. WUQuerySortField *filters, // NULL or list of fields to filter on (terminated by WUSFterm)
  5035. const void *filterbuf, // (appended) string values for filters
  5036. unsigned startoffset,
  5037. unsigned maxnum,
  5038. __int64 *cachehint,
  5039. unsigned *total,
  5040. const MapStringTo<bool> *_subset,
  5041. const MapStringTo<bool> *_suspendedQueriesByCluster)
  5042. {
  5043. struct PostFilters
  5044. {
  5045. WUQueryFilterBoolean activatedFilter;
  5046. WUQueryFilterSuspended suspendedFilter;
  5047. PostFilters()
  5048. {
  5049. activatedFilter = WUQFSAll;
  5050. suspendedFilter = WUQFAllQueries;
  5051. };
  5052. } postFilters;
  5053. class CQuerySetQueriesPager : public CSimpleInterface, implements IElementsPager
  5054. {
  5055. StringAttr querySet;
  5056. StringAttr xPath;
  5057. StringAttr sortOrder;
  5058. PostFilters postFilters;
  5059. StringArray unknownAttributes;
  5060. const MapStringTo<bool> *subset;
  5061. const MapStringTo<bool> *suspendedQueriesByCluster;
  5062. void populateQueryTree(const IPropertyTree* querySetTree, IPropertyTree* queryTree)
  5063. {
  5064. const char* querySetId = querySetTree->queryProp("@id");
  5065. std::unordered_set<std::string> aliasSet;
  5066. Owned<IPropertyTreeIterator> aliasIter = querySetTree->getElements("Alias");
  5067. ForEach(*aliasIter)
  5068. {
  5069. const char *id = aliasIter->query().queryProp("@id");
  5070. if (!isEmptyString(id))
  5071. aliasSet.insert(id);
  5072. }
  5073. VStringBuffer path("Query%s", xPath.get());
  5074. Owned<IPropertyTreeIterator> iter = querySetTree->getElements(path.str());
  5075. ForEach(*iter)
  5076. {
  5077. IPropertyTree &query = iter->query();
  5078. bool activated = false;
  5079. const char* queryId = query.queryProp("@id");
  5080. if (queryId && *queryId)
  5081. {
  5082. if (subset)
  5083. {
  5084. VStringBuffer match("%s/%s", querySetId, queryId);
  5085. if (!subset->getValue(match))
  5086. continue;
  5087. }
  5088. if (aliasSet.find(queryId) != aliasSet.end())
  5089. activated = true;
  5090. }
  5091. if (activated && (postFilters.activatedFilter == WUQFSNo))
  5092. continue;
  5093. if (!activated && (postFilters.activatedFilter == WUQFSYes))
  5094. continue;
  5095. bool isSuspendedByUser = query.hasProp(getEnumText(WUQSFSuspendedByUser,querySortFields));
  5096. bool skip = false;
  5097. switch(postFilters.suspendedFilter)
  5098. {
  5099. case WUQFSUSPDNo:
  5100. if (isSuspendedByUser || checkSuspendedByCluster(querySetId, queryId))
  5101. skip = true;
  5102. break;
  5103. case WUQFSUSPDYes:
  5104. if (!isSuspendedByUser && !checkSuspendedByCluster(querySetId, queryId))
  5105. skip = true;
  5106. break;
  5107. case WUQFSUSPDByUser:
  5108. if (!isSuspendedByUser)
  5109. skip = true;
  5110. break;
  5111. case WUQFSUSPDByFirstNode:
  5112. case WUQFSUSPDByAnyNode:
  5113. if (!checkSuspendedByCluster(querySetId, queryId))
  5114. skip = true;
  5115. break;
  5116. }
  5117. if (skip)
  5118. continue;
  5119. IPropertyTree *queryWithSetId = queryTree->addPropTree("Query", createPTreeFromIPT(&query));
  5120. queryWithSetId->setProp("@querySetId", querySetId);
  5121. queryWithSetId->setPropBool("@activated", activated);
  5122. }
  5123. }
  5124. IRemoteConnection* populateQueryTree(IPropertyTree* queryTree)
  5125. {
  5126. StringBuffer querySetXPath("QuerySets");
  5127. if (!querySet.isEmpty())
  5128. querySetXPath.appendf("/QuerySet[@id=\"%s\"]", querySet.get());
  5129. Owned<IRemoteConnection> conn = querySDS().connect(querySetXPath.str(), myProcessSession(), 0, SDS_LOCK_TIMEOUT);
  5130. if (!conn)
  5131. return NULL;
  5132. IPropertyTree *root = conn->queryRoot()->queryBranch(nullptr);
  5133. if (querySet.isEmpty())
  5134. {
  5135. Owned<IPropertyTreeIterator> querySetIter = root->getElements("*");
  5136. ForEach(*querySetIter)
  5137. populateQueryTree(&querySetIter->query(), queryTree);
  5138. }
  5139. else
  5140. populateQueryTree(root, queryTree);
  5141. return conn.getClear();
  5142. }
  5143. bool checkSuspendedByCluster(const char *querySetId, const char *queryId)
  5144. {
  5145. if (!suspendedQueriesByCluster)
  5146. return false;
  5147. VStringBuffer match("%s/%s", querySetId, queryId);
  5148. bool *found = suspendedQueriesByCluster->getValue(match);
  5149. return (found && *found);
  5150. }
  5151. public:
  5152. IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
  5153. CQuerySetQueriesPager(const char* _querySet, const char* _xPath, const char *_sortOrder, PostFilters& _postFilters,
  5154. StringArray& _unknownAttributes, const MapStringTo<bool> *_subset, const MapStringTo<bool> *_suspendedQueriesByCluster)
  5155. : querySet(_querySet), xPath(_xPath), sortOrder(_sortOrder), subset(_subset), suspendedQueriesByCluster(_suspendedQueriesByCluster)
  5156. {
  5157. postFilters.activatedFilter = _postFilters.activatedFilter;
  5158. postFilters.suspendedFilter = _postFilters.suspendedFilter;
  5159. ForEachItemIn(x, _unknownAttributes)
  5160. unknownAttributes.append(_unknownAttributes.item(x));
  5161. }
  5162. virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree> &elements)
  5163. {
  5164. Owned<IPropertyTree> elementTree = createPTree("Queries");
  5165. Owned<IRemoteConnection> conn = populateQueryTree(elementTree);
  5166. if (!conn)
  5167. return NULL;
  5168. Owned<IPropertyTreeIterator> iter = elementTree->getElements("*");
  5169. if (!iter)
  5170. return NULL;
  5171. sortElements(iter, sortOrder.get(), NULL, NULL, unknownAttributes, elements);
  5172. return conn.getClear();
  5173. }
  5174. virtual bool allMatchingElementsReceived() { return true; } //For now, dali always returns all of matched Queries.
  5175. };
  5176. StringAttr querySet;
  5177. StringBuffer xPath;
  5178. StringBuffer so;
  5179. StringArray unknownAttributes;
  5180. if (filters)
  5181. {
  5182. const char *fv = (const char *)filterbuf;
  5183. for (unsigned i=0;filters[i]!=WUQSFterm;i++) {
  5184. int fmt = filters[i];
  5185. int subfmt = (fmt&0xff);
  5186. if (subfmt==WUQSFQuerySet)
  5187. querySet.set(fv);
  5188. else if ((subfmt==WUQSFmemoryLimit) || (subfmt==WUQSFtimeLimit) || (subfmt==WUQSFwarnTimeLimit) || (subfmt==WUQSFpriority))
  5189. xPath.append('[').append(getEnumText(subfmt,querySortFields)).append(">=").append(fv).append("]");
  5190. else if ((subfmt==WUQSFmemoryLimitHi) || (subfmt==WUQSFtimeLimitHi) || (subfmt==WUQSFwarnTimeLimitHi) || (subfmt==WUQSFpriorityHi))
  5191. xPath.append('[').append(getEnumText(subfmt,querySortFields)).append("<=").append(fv).append("]");
  5192. else if (subfmt==WUQSFActivited)
  5193. postFilters.activatedFilter = (WUQueryFilterBoolean) atoi(fv);
  5194. else if (subfmt==WUQSFSuspendedFilter)
  5195. postFilters.suspendedFilter = (WUQueryFilterSuspended) atoi(fv);
  5196. else if (!fv || !*fv)
  5197. unknownAttributes.append(getEnumText(subfmt,querySortFields));
  5198. else {
  5199. xPath.append('[').append(getEnumText(subfmt,querySortFields)).append('=');
  5200. if (fmt&WUQSFnocase)
  5201. xPath.append('?');
  5202. if (fmt&WUQSFnumeric)
  5203. xPath.append('#');
  5204. if (fmt&WUQSFwild)
  5205. xPath.append('~');
  5206. xPath.append('"').append(fv).append("\"]");
  5207. }
  5208. fv = fv + strlen(fv)+1;
  5209. }
  5210. }
  5211. if (sortorder) {
  5212. for (unsigned i=0;sortorder[i]!=WUQSFterm;i++) {
  5213. if (so.length())
  5214. so.append(',');
  5215. int fmt = sortorder[i];
  5216. if (fmt&WUQSFreverse)
  5217. so.append('-');
  5218. if (fmt&WUQSFnocase)
  5219. so.append('?');
  5220. if (fmt&WUQSFnumeric)
  5221. so.append('#');
  5222. so.append(getEnumText(fmt&0xff,querySortFields));
  5223. }
  5224. }
  5225. IArrayOf<IPropertyTree> results;
  5226. Owned<IElementsPager> elementsPager = new CQuerySetQueriesPager(querySet.get(), xPath.str(), so.length()?so.str():NULL,
  5227. postFilters, unknownAttributes, _subset, _suspendedQueriesByCluster);
  5228. Owned<IRemoteConnection> conn=getElementsPaged(elementsPager,startoffset,maxnum,NULL,"",cachehint,results,total,NULL);
  5229. return new CConstQuerySetQueryIterator(results);
  5230. }
  5231. bool CWorkUnitFactory::isAborting(const char *wuid) const
  5232. {
  5233. VStringBuffer apath("/WorkUnitAborts/%s", wuid);
  5234. try
  5235. {
  5236. Owned<IRemoteConnection> acon = querySDS().connect(apath.str(), myProcessSession(), 0, SDS_LOCK_TIMEOUT);
  5237. if (acon)
  5238. return acon->queryRoot()->getPropInt(NULL) != 0;
  5239. }
  5240. catch (IException *E)
  5241. {
  5242. EXCLOG(E);
  5243. E->Release();
  5244. }
  5245. return false;
  5246. }
  5247. void CWorkUnitFactory::clearAborting(const char *wuid)
  5248. {
  5249. VStringBuffer apath("/WorkUnitAborts/%s", wuid);
  5250. try
  5251. {
  5252. Owned<IRemoteConnection> acon = querySDS().connect(apath.str(), myProcessSession(), RTM_LOCK_WRITE|RTM_LOCK_SUB, SDS_LOCK_TIMEOUT);
  5253. if (acon)
  5254. acon->close(true);
  5255. }
  5256. catch (IException *E)
  5257. {
  5258. EXCLOG(E);
  5259. E->Release();
  5260. }
  5261. }
  5262. void CWorkUnitFactory::reportAbnormalTermination(const char *wuid, WUState &state, SessionId agent)
  5263. {
  5264. WARNLOG("reportAbnormalTermination: session stopped unexpectedly: %" I64F "d state: %d", (__int64) agent, (int) state);
  5265. bool isEcl = false;
  5266. switch (state)
  5267. {
  5268. case WUStateAborting:
  5269. state = WUStateAborted;
  5270. break;
  5271. case WUStateCompiling:
  5272. isEcl = true;
  5273. // drop into
  5274. default:
  5275. state = WUStateFailed;
  5276. }
  5277. Owned<IWorkUnit> wu = updateWorkUnit(wuid, NULL, NULL);
  5278. wu->setState(state);
  5279. Owned<IWUException> e = wu->createException();
  5280. e->setExceptionCode(isEcl ? 1001 : 1000);
  5281. e->setExceptionMessage(isEcl ? "EclCC terminated unexpectedly" : "Workunit terminated unexpectedly");
  5282. }
  5283. static CriticalSection deleteDllLock;
  5284. static IWorkQueueThread *deleteDllWorkQ = nullptr;
  5285. MODULE_INIT(INIT_PRIORITY_STANDARD)
  5286. {
  5287. return true;
  5288. }
  5289. MODULE_EXIT()
  5290. {
  5291. CriticalBlock b(deleteDllLock);
  5292. if (deleteDllWorkQ)
  5293. ::Release(deleteDllWorkQ);
  5294. deleteDllWorkQ = nullptr;
  5295. }
  5296. static void asyncRemoveDll(const char * name)
  5297. {
  5298. CriticalBlock b(deleteDllLock);
  5299. if (!deleteDllWorkQ)
  5300. deleteDllWorkQ = createWorkQueueThread();
  5301. deleteDllWorkQ->post(new asyncRemoveDllWorkItem(name));
  5302. }
  5303. static void asyncRemoveFile(const char * ip, const char * name)
  5304. {
  5305. CriticalBlock b(deleteDllLock);
  5306. if (!deleteDllWorkQ)
  5307. deleteDllWorkQ = createWorkQueueThread();
  5308. deleteDllWorkQ->post(new asyncRemoveRemoteFileWorkItem(ip, name));
  5309. }
  5310. class CDaliWorkUnitFactory : public CWorkUnitFactory, implements IDaliClientShutdown
  5311. {
  5312. public:
  5313. IMPLEMENT_IINTERFACE_USING(CWorkUnitFactory);
  5314. CDaliWorkUnitFactory()
  5315. {
  5316. // Assumes dali client configuration has already been done
  5317. sdsManager = &querySDS();
  5318. session = myProcessSession();
  5319. addShutdownHook(*this);
  5320. }
  5321. ~CDaliWorkUnitFactory()
  5322. {
  5323. removeShutdownHook(*this);
  5324. }
  5325. virtual bool initializeStore()
  5326. {
  5327. throwUnexpected(); // Used when loading a plugin factory - not applicable here
  5328. }
  5329. virtual IWorkUnitWatcher *getWatcher(IWorkUnitSubscriber *subscriber, WUSubscribeOptions options, const char *wuid) const
  5330. {
  5331. return new CDaliWorkUnitWatcher(subscriber, options, wuid);
  5332. }
  5333. virtual unsigned validateRepository(bool fixErrors)
  5334. {
  5335. return 0;
  5336. }
  5337. virtual void deleteRepository(bool recreate)
  5338. {
  5339. Owned<IRemoteConnection> conn = sdsManager->connect("/WorkUnits", session, RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  5340. if (conn)
  5341. conn->close(true);
  5342. conn.setown(sdsManager->connect("/GraphProgress", session, RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
  5343. if (conn)
  5344. conn->close(true);
  5345. }
  5346. virtual void createRepository()
  5347. {
  5348. // Nothing to do
  5349. }
  5350. virtual const char *queryStoreType() const
  5351. {
  5352. return "Dali";
  5353. }
  5354. virtual CLocalWorkUnit *_createWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  5355. {
  5356. StringBuffer wuRoot;
  5357. getXPath(wuRoot, wuid);
  5358. IRemoteConnection *conn;
  5359. conn = sdsManager->connect(wuRoot.str(), session, RTM_LOCK_WRITE|RTM_CREATE_UNIQUE, SDS_LOCK_TIMEOUT);
  5360. conn->queryRoot()->setProp("@xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance");
  5361. conn->queryRoot()->setPropInt("@wuidVersion", WUID_VERSION);
  5362. conn->queryRoot()->setProp("@totalThorTime", "");
  5363. return new CDaliWorkUnit(conn, secmgr, secuser);
  5364. }
  5365. virtual CLocalWorkUnit* _openWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  5366. {
  5367. StringBuffer wuRoot;
  5368. getXPath(wuRoot, wuid);
  5369. IRemoteConnection* conn = sdsManager->connect(wuRoot.str(), session, 0, SDS_LOCK_TIMEOUT);
  5370. if (conn)
  5371. return new CDaliWorkUnit(conn, secmgr, secuser);
  5372. else
  5373. return NULL;
  5374. }
  5375. virtual bool _restoreWorkUnit(IPTree *_pt, const char *wuid)
  5376. {
  5377. Owned<IPTree> pt(_pt);
  5378. Owned<IPropertyTree> gprogress = pruneBranch(pt, "GraphProgress[1]");
  5379. StringBuffer wuRoot;
  5380. getXPath(wuRoot, wuid);
  5381. Owned<IRemoteConnection> conn = sdsManager->connect(wuRoot.str(), myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  5382. if (!conn)
  5383. {
  5384. ERRLOG("restoreWorkUnit could not create to %s", wuRoot.str());
  5385. return false;
  5386. }
  5387. IPropertyTree *root = conn->queryRoot();
  5388. if (root->hasChildren())
  5389. {
  5390. ERRLOG("restoreWorkUnit WUID %s already exists", wuid);
  5391. return false;
  5392. }
  5393. root->setPropTree(NULL, pt.getClear());
  5394. conn.clear();
  5395. // now kludge back GraphProgress
  5396. if (gprogress)
  5397. {
  5398. VStringBuffer xpath("/GraphProgress/%s", wuid);
  5399. conn.setown(querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT));
  5400. if (conn)
  5401. {
  5402. IPropertyTree *groot = conn->queryRoot();
  5403. if (groot->hasChildren())
  5404. WARNLOG("restoreWorkUnit WUID %s graphprogress already exists, replacing",wuid);
  5405. groot->setPropTree(NULL, gprogress.getClear());
  5406. }
  5407. }
  5408. return true;
  5409. }
  5410. virtual CLocalWorkUnit* _updateWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  5411. {
  5412. StringBuffer wuRoot;
  5413. getXPath(wuRoot, wuid);
  5414. IRemoteConnection* conn = sdsManager->connect(wuRoot.str(), session, RTM_LOCK_WRITE|RTM_LOCK_SUB, SDS_LOCK_TIMEOUT);
  5415. if (conn)
  5416. return new CDaliWorkUnit(conn, secmgr, secuser);
  5417. else
  5418. return NULL;
  5419. }
  5420. virtual IWorkUnit* getGlobalWorkUnit(ISecManager *secmgr, ISecUser *secuser)
  5421. {
  5422. // MORE - should it check security?
  5423. StringBuffer wuRoot;
  5424. getXPath(wuRoot, GLOBAL_WORKUNIT);
  5425. IRemoteConnection* conn = sdsManager->connect(wuRoot.str(), session, RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  5426. conn->queryRoot()->setProp("@xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance");
  5427. Owned<CLocalWorkUnit> cw = new CDaliWorkUnit(conn, (ISecManager *) NULL, NULL);
  5428. return &cw->lockRemote(false);
  5429. }
  5430. virtual IConstWorkUnitIterator* getWorkUnitsByOwner(const char * owner, ISecManager *secmgr, ISecUser *secuser)
  5431. {
  5432. StringBuffer path("*");
  5433. if (owner && *owner)
  5434. path.append("[@submitID=?~\"").append(owner).append("\"]");
  5435. return _getWorkUnitsByXPath(path.str(), secmgr, secuser);
  5436. }
  5437. IConstWorkUnitIterator* getScheduledWorkUnits(ISecManager *secmgr, ISecUser *secuser)
  5438. {
  5439. StringBuffer path("*");
  5440. path.append("[@state=\"").append(getEnumText(WUStateScheduled, states)).append("\"]");
  5441. return _getWorkUnitsByXPath(path.str(), secmgr, secuser);
  5442. }
  5443. virtual void clientShutdown();
  5444. virtual unsigned numWorkUnits()
  5445. {
  5446. Owned<IRemoteConnection> conn = sdsManager->connect("/WorkUnits", session, 0, SDS_LOCK_TIMEOUT);
  5447. if (!conn)
  5448. return 0;
  5449. IPropertyTree *root = conn->queryRoot();
  5450. return root->numChildren();
  5451. }
  5452. /**
  5453. * Add a filter to an xpath query, with the appropriate filter flags
  5454. */
  5455. static void appendFilterToQueryString(StringBuffer& query, int flags, const char* name, const char* value)
  5456. {
  5457. query.append('[').append(name).append('=');
  5458. if (flags & WUSFnocase)
  5459. query.append('?');
  5460. if (flags & WUSFwild)
  5461. query.append('~');
  5462. query.append('"').append(value).append("\"]");
  5463. };
  5464. IConstWorkUnitIterator* getWorkUnitsSorted( WUSortField sortorder, // field to sort by (and flags for desc sort etc)
  5465. WUSortField *filters, // NULL or list of fields to filter on (terminated by WUSFterm)
  5466. const void *filterbuf, // (appended) string values for filters
  5467. unsigned startoffset,
  5468. unsigned maxnum,
  5469. __int64 *cachehint,
  5470. unsigned *total,
  5471. ISecManager *secmgr,
  5472. ISecUser *secuser)
  5473. {
  5474. class CQueryOrFilter : public CInterface
  5475. {
  5476. unsigned flags;
  5477. StringAttr name;
  5478. StringArray values;
  5479. public:
  5480. IMPLEMENT_IINTERFACE;
  5481. CQueryOrFilter(unsigned _flags, const char *_name, const char *_value)
  5482. : flags(_flags), name(_name)
  5483. {
  5484. values.appendListUniq(_value, "|");
  5485. };
  5486. const char* queryName() { return name.get(); };
  5487. unsigned querySearchFlags() { return flags; };
  5488. const StringArray& queryValues() const { return values; };
  5489. };
  5490. class CMultiPTreeIterator : public CInterfaceOf<IPropertyTreeIterator>
  5491. {
  5492. public:
  5493. virtual bool first() override
  5494. {
  5495. curSource = 0;
  5496. while (sources.isItem(curSource))
  5497. {
  5498. if (sources.item(curSource).first())
  5499. return true;
  5500. curSource++;
  5501. }
  5502. return false;
  5503. }
  5504. virtual bool next() override
  5505. {
  5506. if (sources.isItem(curSource))
  5507. {
  5508. if (sources.item(curSource).next())
  5509. return true;
  5510. curSource++;
  5511. while (sources.isItem(curSource))
  5512. {
  5513. if (sources.item(curSource).first())
  5514. return true;
  5515. curSource++;
  5516. }
  5517. }
  5518. return false;
  5519. }
  5520. virtual bool isValid() override
  5521. {
  5522. return sources.isItem(curSource);
  5523. }
  5524. virtual IPropertyTree & query() override
  5525. {
  5526. return sources.item(curSource).query();
  5527. }
  5528. void addSource(IPropertyTreeIterator &source)
  5529. {
  5530. sources.append(source);
  5531. }
  5532. private:
  5533. IArrayOf<IPropertyTreeIterator> sources;
  5534. unsigned curSource = 0;
  5535. };
  5536. class CWorkUnitsPager : public CSimpleInterface, implements IElementsPager
  5537. {
  5538. StringAttr xPath;
  5539. StringAttr sortOrder;
  5540. StringAttr nameFilterLo;
  5541. StringAttr nameFilterHi;
  5542. StringArray unknownAttributes;
  5543. Owned<CQueryOrFilter> orFilter;
  5544. public:
  5545. IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
  5546. CWorkUnitsPager(const char* _xPath, CQueryOrFilter* _orFilter, const char *_sortOrder, const char* _nameFilterLo, const char* _nameFilterHi, StringArray& _unknownAttributes)
  5547. : xPath(_xPath), orFilter(_orFilter), sortOrder(_sortOrder), nameFilterLo(_nameFilterLo), nameFilterHi(_nameFilterHi)
  5548. {
  5549. ForEachItemIn(x, _unknownAttributes)
  5550. unknownAttributes.append(_unknownAttributes.item(x));
  5551. }
  5552. virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree> &elements)
  5553. {
  5554. Owned<IRemoteConnection> conn = querySDS().connect("WorkUnits", myProcessSession(), 0, SDS_LOCK_TIMEOUT);
  5555. if (!conn)
  5556. return NULL;
  5557. Owned<IPropertyTreeIterator> iter;
  5558. if (!orFilter)
  5559. {
  5560. iter.setown(conn->getElements(xPath.get()));
  5561. }
  5562. else
  5563. {
  5564. Owned <CMultiPTreeIterator> multi = new CMultiPTreeIterator;
  5565. bool added = false;
  5566. const char* fieldName = orFilter->queryName();
  5567. unsigned flags = orFilter->querySearchFlags();
  5568. const StringArray& values = orFilter->queryValues();
  5569. ForEachItemIn(i, values)
  5570. {
  5571. StringBuffer path(xPath.get());
  5572. const char* value = values.item(i);
  5573. if (!isEmptyString(value))
  5574. {
  5575. appendFilterToQueryString(path, flags, fieldName, value);
  5576. IPropertyTreeIterator *itr = conn->getElements(path.str());
  5577. if (itr)
  5578. {
  5579. multi->addSource(*itr);
  5580. added = true;
  5581. }
  5582. }
  5583. }
  5584. if (added)
  5585. iter.setown(multi.getClear());
  5586. }
  5587. if (!iter)
  5588. return NULL;
  5589. sortElements(iter, sortOrder.get(), nameFilterLo.get(), nameFilterHi.get(), unknownAttributes, elements);
  5590. return conn.getClear();
  5591. }
  5592. virtual bool allMatchingElementsReceived() { return true; }//For now, dali always returns all of matched WUs.
  5593. };
  5594. class CScopeChecker : public CSimpleInterface, implements ISortedElementsTreeFilter
  5595. {
  5596. UniqueScopes done;
  5597. ISecManager *secmgr;
  5598. ISecUser *secuser;
  5599. CriticalSection crit;
  5600. public:
  5601. IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
  5602. CScopeChecker(ISecManager *_secmgr,ISecUser *_secuser)
  5603. {
  5604. secmgr = _secmgr;
  5605. secuser = _secuser;
  5606. }
  5607. bool isOK(IPropertyTree &tree)
  5608. {
  5609. const char *scopename = tree.queryProp("@scope");
  5610. if (!scopename||!*scopename)
  5611. return true;
  5612. {
  5613. CriticalBlock block(crit);
  5614. const bool *b = done.getValue(scopename);
  5615. if (b)
  5616. return *b;
  5617. }
  5618. bool ret = checkWuScopeSecAccess(scopename,secmgr,secuser,SecAccess_Read,"iterating",false,false);
  5619. {
  5620. // conceivably could have already been checked and added, but ok.
  5621. CriticalBlock block(crit);
  5622. done.setValue(scopename,ret);
  5623. }
  5624. return ret;
  5625. }
  5626. };
  5627. Owned<CQueryOrFilter> orFilter;
  5628. Owned<ISortedElementsTreeFilter> sc = new CScopeChecker(secmgr,secuser);
  5629. StringBuffer query;
  5630. StringBuffer so;
  5631. StringAttr namefilter("*");
  5632. StringAttr namefilterlo;
  5633. StringAttr namefilterhi;
  5634. StringArray unknownAttributes;
  5635. if (filters)
  5636. {
  5637. const char *fv = (const char *) filterbuf;
  5638. for (unsigned i=0;filters[i]!=WUSFterm;i++)
  5639. {
  5640. assertex(fv);
  5641. int fmt = filters[i];
  5642. int subfmt = (fmt&0xff);
  5643. if (subfmt==WUSFwuid)
  5644. namefilterlo.set(fv);
  5645. else if (subfmt==WUSFwuidhigh)
  5646. namefilterhi.set(fv);
  5647. else if (subfmt==WUSFwildwuid)
  5648. namefilter.set(fv);
  5649. else if (subfmt==WUSFappvalue)
  5650. {
  5651. const char *app = fv;
  5652. fv = fv + strlen(fv)+1;
  5653. query.append("[Application/").append(app);
  5654. if (*fv)
  5655. query.append("=?~\"").append(fv).append('\"');
  5656. query.append("]");
  5657. }
  5658. else if (subfmt==WUSFtotalthortime)
  5659. {
  5660. query.append("[@totalThorTime>=\"");
  5661. formatTimeCollatable(query, milliToNano(atoi(fv)), false);
  5662. query.append("\"]");
  5663. }
  5664. else if (!*fv)
  5665. {
  5666. unknownAttributes.append(getEnumText(subfmt,workunitSortFields));
  5667. if (subfmt==WUSFtotalthortime)
  5668. sortorder = (WUSortField) (sortorder & ~WUSFnumeric);
  5669. }
  5670. else
  5671. {
  5672. const char* fieldName = getEnumText(subfmt,workunitSortFields);
  5673. if (!strchr(fv, '|'))
  5674. appendFilterToQueryString(query, fmt, fieldName, fv);
  5675. else if (orFilter)
  5676. throw MakeStringException(WUERR_InvalidUserInput, "Multiple OR filters not allowed");
  5677. else
  5678. {
  5679. if (!strieq(fieldName, getEnumText(WUSFstate,workunitSortFields)) &&
  5680. !strieq(fieldName, getEnumText(WUSFuser,workunitSortFields)) &&
  5681. !strieq(fieldName, getEnumText(WUSFcluster,workunitSortFields)))
  5682. throw MakeStringException(WUERR_InvalidUserInput, "OR filters not allowed for %s", fieldName);
  5683. orFilter.setown(new CQueryOrFilter(fmt, fieldName, fv));
  5684. }
  5685. }
  5686. fv = fv + strlen(fv)+1;
  5687. }
  5688. }
  5689. if ((sortorder&0xff)==WUSFtotalthortime)
  5690. sortorder = (WUSortField) (sortorder & ~WUSFnumeric);
  5691. query.insert(0, namefilter.get());
  5692. if (sortorder)
  5693. {
  5694. if (so.length())
  5695. so.append(',');
  5696. if (sortorder & WUSFreverse)
  5697. so.append('-');
  5698. if (sortorder & WUSFnocase)
  5699. so.append('?');
  5700. if (sortorder & WUSFnumeric)
  5701. so.append('#');
  5702. so.append(getEnumText(sortorder&0xff,workunitSortFields));
  5703. }
  5704. IArrayOf<IPropertyTree> results;
  5705. Owned<IElementsPager> elementsPager = new CWorkUnitsPager(query.str(), orFilter.getClear(), so.length()?so.str():NULL, namefilterlo.get(), namefilterhi.get(), unknownAttributes);
  5706. Owned<IRemoteConnection> conn=getElementsPaged(elementsPager,startoffset,maxnum,secmgr?sc:NULL,"",cachehint,results,total,NULL);
  5707. return new CConstWUArrayIterator(results);
  5708. }
  5709. virtual WUState waitForWorkUnit(const char * wuid, unsigned timeout, bool compiled, std::list<WUState> expectedStates)
  5710. {
  5711. WUState ret = WUStateUnknown;
  5712. StringBuffer wuRoot;
  5713. getXPath(wuRoot, wuid);
  5714. Owned<IRemoteConnection> conn = sdsManager->connect(wuRoot.str(), session, 0, SDS_LOCK_TIMEOUT);
  5715. if (timeout == 0) //no need to subscribe
  5716. {
  5717. ret = (WUState) getEnum(conn->queryRoot(), "@state", states);
  5718. auto it = std::find(expectedStates.begin(), expectedStates.end(), ret);
  5719. if (it != expectedStates.end())
  5720. return ret;
  5721. switch (ret)
  5722. {
  5723. case WUStateCompiled:
  5724. case WUStateUploadingFiles:
  5725. if (!compiled)
  5726. break;
  5727. //fall through
  5728. case WUStateCompleted:
  5729. case WUStateFailed:
  5730. case WUStateAborted:
  5731. return ret;
  5732. default:
  5733. break;
  5734. }
  5735. return WUStateUnknown;
  5736. }
  5737. Owned<WorkUnitWaiter> waiter = new WorkUnitWaiter(wuid, SubscribeOptionState);
  5738. LocalIAbortHandler abortHandler(*waiter);
  5739. if (conn)
  5740. {
  5741. SessionId agent = -1;
  5742. bool agentSessionStopped = false;
  5743. unsigned start = msTick();
  5744. for (;;)
  5745. {
  5746. ret = (WUState) getEnum(conn->queryRoot(), "@state", states);
  5747. auto it = std::find(expectedStates.begin(), expectedStates.end(), ret);
  5748. if (it != expectedStates.end())
  5749. return ret;
  5750. switch (ret)
  5751. {
  5752. case WUStateCompiled:
  5753. case WUStateUploadingFiles:
  5754. if (!compiled)
  5755. break;
  5756. // fall into
  5757. case WUStateCompleted:
  5758. case WUStateFailed:
  5759. case WUStateAborted:
  5760. return ret;
  5761. case WUStateWait:
  5762. break;
  5763. case WUStateCompiling:
  5764. case WUStateRunning:
  5765. case WUStateDebugPaused:
  5766. case WUStateDebugRunning:
  5767. case WUStateBlocked:
  5768. case WUStateAborting:
  5769. if (agentSessionStopped)
  5770. {
  5771. reportAbnormalTermination(wuid, ret, agent);
  5772. return ret;
  5773. }
  5774. if (queryDaliServerVersion().compare("2.1")>=0)
  5775. {
  5776. agent = conn->queryRoot()->getPropInt64("@agentSession", -1);
  5777. if((agent>0) && querySessionManager().sessionStopped(agent, 0))
  5778. {
  5779. agentSessionStopped = true;
  5780. conn->reload();
  5781. continue;
  5782. }
  5783. }
  5784. break;
  5785. }
  5786. agentSessionStopped = false; // reset for state changes such as WUStateWait then WUStateRunning again
  5787. unsigned waited = msTick() - start;
  5788. if (timeout==-1 || waited + 20000 < timeout)
  5789. {
  5790. waiter->wait(20000); // recheck state every 20 seconds, in case eclagent has crashed.
  5791. if (waiter->isAborted())
  5792. {
  5793. ret = WUStateUnknown; // MORE - throw an exception?
  5794. break;
  5795. }
  5796. }
  5797. else if (waited > timeout || !waiter->wait(timeout-waited))
  5798. {
  5799. ret = WUStateUnknown; // MORE - throw an exception?
  5800. break;
  5801. }
  5802. conn->reload();
  5803. }
  5804. }
  5805. return ret;
  5806. }
  5807. virtual WUAction waitForWorkUnitAction(const char * wuid, WUAction original)
  5808. {
  5809. Owned<WorkUnitWaiter> waiter = new WorkUnitWaiter(wuid, SubscribeOptionAction);
  5810. LocalIAbortHandler abortHandler(*waiter);
  5811. WUAction ret = WUActionUnknown;
  5812. StringBuffer wuRoot;
  5813. getXPath(wuRoot, wuid);
  5814. Owned<IRemoteConnection> conn = sdsManager->connect(wuRoot.str(), session, 0, SDS_LOCK_TIMEOUT);
  5815. if (conn)
  5816. {
  5817. unsigned start = msTick();
  5818. for (;;)
  5819. {
  5820. ret = (WUAction) getEnum(conn->queryRoot(), "Action", actions);
  5821. if (ret != original)
  5822. break;
  5823. unsigned waited = msTick() - start;
  5824. waiter->wait(20000); // recheck state every 20 seconds even if no timeout, in case eclagent has crashed.
  5825. if (waiter->isAborted())
  5826. {
  5827. ret = WUActionUnknown; // MORE - throw an exception?
  5828. break;
  5829. }
  5830. conn->reload();
  5831. }
  5832. }
  5833. waiter->unsubscribe();
  5834. return ret;
  5835. }
  5836. protected:
  5837. IConstWorkUnitIterator * _getWorkUnitsByXPath(const char *xpath, ISecManager *secmgr, ISecUser *secuser)
  5838. {
  5839. Owned<IRemoteConnection> conn = sdsManager->connect("/WorkUnits", session, 0, SDS_LOCK_TIMEOUT);
  5840. if (conn)
  5841. {
  5842. CDaliVersion serverVersionNeeded("3.2");
  5843. Owned<IPropertyTreeIterator> iter(queryDaliServerVersion().compare(serverVersionNeeded) < 0 ?
  5844. conn->queryRoot()->getElements(xpath) :
  5845. conn->getElements(xpath));
  5846. return createSecureConstWUIterator(iter.getClear(), secmgr, secuser);
  5847. }
  5848. else
  5849. return NULL;
  5850. }
  5851. ISDSManager *sdsManager;
  5852. SessionId session;
  5853. };
  5854. extern WORKUNIT_API IConstWorkUnitIterator *createSecureConstWUIterator(IConstWorkUnitIterator *iter, ISecManager *secmgr, ISecUser *secuser)
  5855. {
  5856. if (secmgr)
  5857. return new CSecureConstWUIterator(iter, secmgr, secuser);
  5858. else
  5859. return iter;
  5860. }
  5861. extern WORKUNIT_API IConstWorkUnitIterator *createSecureConstWUIterator(IPropertyTreeIterator *iter, ISecManager *secmgr, ISecUser *secuser)
  5862. {
  5863. if (secmgr)
  5864. return new CSecureConstWUIterator(new CConstWUIterator(iter), secmgr, secuser);
  5865. else
  5866. return new CConstWUIterator(iter);
  5867. }
  5868. static CriticalSection factoryCrit;
  5869. static Owned<IWorkUnitFactory> globalFactory;
  5870. void CDaliWorkUnitFactory::clientShutdown()
  5871. {
  5872. CriticalBlock b(factoryCrit);
  5873. globalFactory.clear();
  5874. }
  5875. void clientShutdownWorkUnit()
  5876. {
  5877. CriticalBlock b(factoryCrit);
  5878. globalFactory.clear();
  5879. }
  5880. extern WORKUNIT_API void setWorkUnitFactory(IWorkUnitFactory * _factory)
  5881. {
  5882. CriticalBlock b(factoryCrit);
  5883. globalFactory.setown(_factory);
  5884. }
  5885. extern WORKUNIT_API IWorkUnitFactory * getWorkUnitFactory()
  5886. {
  5887. if (!globalFactory)
  5888. {
  5889. CriticalBlock b(factoryCrit);
  5890. if (!globalFactory) // NOTE - this "double test" paradigm is not guaranteed threadsafe on modern systems/compilers - I think in this instance that is harmless even in the (extremely) unlikely event that it resulted in the setown being called twice.
  5891. {
  5892. const char *forceEnv = getenv("FORCE_DALI_WORKUNITS");
  5893. bool forceDali = forceEnv && !strieq(forceEnv, "off") && !strieq(forceEnv, "0");
  5894. Owned<IRemoteConnection> env = querySDS().connect("/Environment", myProcessSession(), 0, SDS_LOCK_TIMEOUT);
  5895. IPropertyTree *pluginInfo = NULL;
  5896. if (env)
  5897. {
  5898. SocketEndpoint targetDali = queryCoven().queryGroup().queryNode(0).endpoint();
  5899. IPropertyTree *daliInfo = findDaliProcess(env->queryRoot(), targetDali);
  5900. if (daliInfo)
  5901. {
  5902. const char *daliName = daliInfo->queryProp("@name");
  5903. if (daliName)
  5904. {
  5905. VStringBuffer xpath("Software/DaliServerPlugin[@type='WorkunitServer'][@daliServers='%s']", daliName);
  5906. pluginInfo = env->queryRoot()->queryPropTree(xpath);
  5907. }
  5908. if (!pluginInfo)
  5909. pluginInfo = daliInfo->queryPropTree("Plugin[@type='WorkunitServer']"); // Compatibility with early betas of 6.0 ...
  5910. }
  5911. }
  5912. if (pluginInfo && !forceDali)
  5913. globalFactory.setown( (IWorkUnitFactory *) loadPlugin(pluginInfo));
  5914. else
  5915. globalFactory.setown(new CDaliWorkUnitFactory());
  5916. }
  5917. }
  5918. return globalFactory.getLink();
  5919. }
  5920. extern WORKUNIT_API IWorkUnitFactory * getDaliWorkUnitFactory()
  5921. {
  5922. if (!globalFactory)
  5923. {
  5924. CriticalBlock b(factoryCrit);
  5925. if (!globalFactory) // NOTE - this "double test" paradigm is not guaranteed threadsafe on modern systems/compilers - I think in this instance that is harmless even in the (extremely) unlikely event that it resulted in the setown being called twice.
  5926. globalFactory.setown(new CDaliWorkUnitFactory());
  5927. }
  5928. return globalFactory.getLink();
  5929. }
  5930. // A SecureWorkUnitFactory allows the security params to be supplied once to the factory rather than being supplied to each call.
  5931. // They can still be supplied if you want...
  5932. class CSecureWorkUnitFactory : implements IWorkUnitFactory, public CInterface
  5933. {
  5934. public:
  5935. IMPLEMENT_IINTERFACE;
  5936. CSecureWorkUnitFactory(IWorkUnitFactory *_baseFactory, ISecManager *_secMgr, ISecUser *_secUser)
  5937. : baseFactory(_baseFactory), defaultSecMgr(_secMgr), defaultSecUser(_secUser)
  5938. {
  5939. }
  5940. virtual bool initializeStore()
  5941. {
  5942. throwUnexpected(); // Used when loading a plugin factory - not applicable here
  5943. }
  5944. virtual IWorkUnitWatcher *getWatcher(IWorkUnitSubscriber *subscriber, WUSubscribeOptions options, const char *wuid) const
  5945. {
  5946. return baseFactory->getWatcher(subscriber, options, wuid);
  5947. }
  5948. virtual unsigned validateRepository(bool fix)
  5949. {
  5950. return baseFactory->validateRepository(fix);
  5951. }
  5952. virtual void deleteRepository(bool recreate)
  5953. {
  5954. return baseFactory->deleteRepository(recreate);
  5955. }
  5956. virtual void createRepository()
  5957. {
  5958. return baseFactory->createRepository();
  5959. }
  5960. virtual const char *queryStoreType() const
  5961. {
  5962. return baseFactory->queryStoreType();
  5963. }
  5964. virtual StringArray &getUniqueValues(WUSortField field, const char *prefix, StringArray &result) const
  5965. {
  5966. return baseFactory->getUniqueValues(field, prefix, result);
  5967. }
  5968. virtual IWorkUnit* createNamedWorkUnit(const char *wuid, const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
  5969. {
  5970. if (!secMgr) secMgr = defaultSecMgr.get();
  5971. if (!secUser) secUser = defaultSecUser.get();
  5972. return baseFactory->createNamedWorkUnit(wuid, app, user, secMgr, secUser);
  5973. }
  5974. virtual IWorkUnit* createWorkUnit(const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
  5975. {
  5976. if (!secMgr) secMgr = defaultSecMgr.get();
  5977. if (!secUser) secUser = defaultSecUser.get();
  5978. return baseFactory->createWorkUnit(app, user, secMgr, secUser);
  5979. }
  5980. virtual bool deleteWorkUnit(const char * wuid, ISecManager *secMgr, ISecUser *secUser)
  5981. {
  5982. if (!secMgr) secMgr = defaultSecMgr.get();
  5983. if (!secUser) secUser = defaultSecUser.get();
  5984. return baseFactory->deleteWorkUnit(wuid, secMgr, secUser);
  5985. }
  5986. virtual bool deleteWorkUnitEx(const char * wuid, bool throwException, ISecManager *secMgr, ISecUser *secUser)
  5987. {
  5988. if (!secMgr) secMgr = defaultSecMgr.get();
  5989. if (!secUser) secUser = defaultSecUser.get();
  5990. return baseFactory->deleteWorkUnitEx(wuid, throwException, secMgr, secUser);
  5991. }
  5992. virtual IConstWorkUnit* openWorkUnit(const char *wuid, ISecManager *secMgr, ISecUser *secUser)
  5993. {
  5994. if (!secMgr) secMgr = defaultSecMgr.get();
  5995. if (!secUser) secUser = defaultSecUser.get();
  5996. return baseFactory->openWorkUnit(wuid, secMgr, secUser);
  5997. }
  5998. virtual IWorkUnit* updateWorkUnit(const char *wuid, ISecManager *secMgr, ISecUser *secUser)
  5999. {
  6000. if (!secMgr) secMgr = defaultSecMgr.get();
  6001. if (!secUser) secUser = defaultSecUser.get();
  6002. return baseFactory->updateWorkUnit(wuid, secMgr, secUser);
  6003. }
  6004. virtual bool restoreWorkUnit(const char *base, const char *wuid, bool restoreAssociated)
  6005. {
  6006. return baseFactory->restoreWorkUnit(base, wuid, restoreAssociated);
  6007. }
  6008. virtual void importWorkUnit(const char *zapReportFileName, const char *zapReportPassword,
  6009. const char *importDir, const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
  6010. {
  6011. baseFactory->importWorkUnit(zapReportFileName, zapReportPassword, importDir, app, user, secMgr, secUser);
  6012. }
  6013. virtual IWorkUnit * getGlobalWorkUnit(ISecManager *secMgr, ISecUser *secUser)
  6014. {
  6015. if (!secMgr) secMgr = defaultSecMgr.get();
  6016. if (!secUser) secUser = defaultSecUser.get();
  6017. return baseFactory->getGlobalWorkUnit(secMgr, secUser);
  6018. }
  6019. virtual IConstWorkUnitIterator * getWorkUnitsByOwner(const char * owner, ISecManager *secMgr, ISecUser *secUser)
  6020. {
  6021. if (!secMgr) secMgr = defaultSecMgr.get();
  6022. if (!secUser) secUser = defaultSecUser.get();
  6023. return baseFactory->getWorkUnitsByOwner(owner, secMgr, secUser);
  6024. }
  6025. virtual IConstWorkUnitIterator * getScheduledWorkUnits(ISecManager *secMgr, ISecUser *secUser)
  6026. {
  6027. if (!secMgr) secMgr = defaultSecMgr.get();
  6028. if (!secUser) secUser = defaultSecUser.get();
  6029. return baseFactory->getScheduledWorkUnits(secMgr, secUser);
  6030. }
  6031. virtual void descheduleAllWorkUnits(ISecManager *secMgr, ISecUser *secUser)
  6032. {
  6033. if (!secMgr) secMgr = defaultSecMgr.get();
  6034. if (!secUser) secUser = defaultSecUser.get();
  6035. baseFactory->descheduleAllWorkUnits(secMgr, secUser);
  6036. }
  6037. virtual int setTracingLevel(int newLevel)
  6038. {
  6039. return baseFactory->setTracingLevel(newLevel);
  6040. }
  6041. virtual IConstWorkUnitIterator* getWorkUnitsSorted( WUSortField sortorder, // field to sort by
  6042. WUSortField *filters, // NULL or list of fields to filter on (terminated by WUSFterm)
  6043. const void *filterbuf, // (appended) string values for filters
  6044. unsigned startoffset,
  6045. unsigned maxnum,
  6046. __int64 *cachehint,
  6047. unsigned *total,
  6048. ISecManager *secMgr, ISecUser *secUser)
  6049. {
  6050. if (!secMgr) secMgr = defaultSecMgr.get();
  6051. if (!secUser) secUser = defaultSecUser.get();
  6052. return baseFactory->getWorkUnitsSorted(sortorder,filters,filterbuf,startoffset,maxnum,cachehint, total, secMgr, secUser);
  6053. }
  6054. virtual IConstQuerySetQueryIterator* getQuerySetQueriesSorted( WUQuerySortField *sortorder,
  6055. WUQuerySortField *filters,
  6056. const void *filterbuf,
  6057. unsigned startoffset,
  6058. unsigned maxnum,
  6059. __int64 *cachehint,
  6060. unsigned *total,
  6061. const MapStringTo<bool> *subset,
  6062. const MapStringTo<bool> *suspendedByCluster)
  6063. {
  6064. // MORE - why no security?
  6065. return baseFactory->getQuerySetQueriesSorted(sortorder,filters,filterbuf,startoffset,maxnum,cachehint,total,subset,suspendedByCluster);
  6066. }
  6067. virtual unsigned numWorkUnits()
  6068. {
  6069. return baseFactory->numWorkUnits();
  6070. }
  6071. virtual bool isAborting(const char *wuid) const
  6072. {
  6073. return baseFactory->isAborting(wuid);
  6074. }
  6075. virtual void clearAborting(const char *wuid)
  6076. {
  6077. baseFactory->clearAborting(wuid);
  6078. }
  6079. virtual WUState waitForWorkUnit(const char * wuid, unsigned timeout, bool compiled, std::list<WUState> expectedStates)
  6080. {
  6081. return baseFactory->waitForWorkUnit(wuid, timeout, compiled, expectedStates);
  6082. }
  6083. virtual WUAction waitForWorkUnitAction(const char * wuid, WUAction original)
  6084. {
  6085. return baseFactory->waitForWorkUnitAction(wuid, original);
  6086. }
  6087. private:
  6088. Owned<IWorkUnitFactory> baseFactory;
  6089. Linked<ISecManager> defaultSecMgr;
  6090. Linked<ISecUser> defaultSecUser;
  6091. };
  6092. extern WORKUNIT_API IWorkUnitFactory * getWorkUnitFactory(ISecManager *secmgr, ISecUser *secuser)
  6093. {
  6094. if (secmgr && secuser)
  6095. return new CSecureWorkUnitFactory(getWorkUnitFactory(), secmgr, secuser);
  6096. else
  6097. return getWorkUnitFactory();
  6098. }
  6099. //==========================================================================================
  6100. class CStringPTreeIterator : implements IStringIterator, public CInterface
  6101. {
  6102. Owned<IPropertyTreeIterator> it;
  6103. public:
  6104. IMPLEMENT_IINTERFACE;
  6105. CStringPTreeIterator(IPropertyTreeIterator *p) : it(p) {};
  6106. virtual bool first() { return it->first(); }
  6107. virtual bool next() { return it->next(); }
  6108. virtual bool isValid() { return it->isValid(); }
  6109. virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryProp(NULL)); return s; }
  6110. };
  6111. class CStringPTreeTagIterator : implements IStringIterator, public CInterface
  6112. {
  6113. Owned<IPropertyTreeIterator> it;
  6114. public:
  6115. IMPLEMENT_IINTERFACE;
  6116. CStringPTreeTagIterator(IPropertyTreeIterator *p) : it(p) {};
  6117. virtual bool first() { return it->first(); }
  6118. virtual bool next() { return it->next(); }
  6119. virtual bool isValid() { return it->isValid(); }
  6120. virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryName()); return s; }
  6121. };
  6122. class CStringPTreeAttrIterator : implements IStringIterator, public CInterface
  6123. {
  6124. Owned<IPropertyTreeIterator> it;
  6125. StringAttr name;
  6126. public:
  6127. IMPLEMENT_IINTERFACE;
  6128. CStringPTreeAttrIterator(IPropertyTreeIterator *p, const char *_name) : it(p), name(_name) {};
  6129. virtual bool first() { return it->first(); }
  6130. virtual bool next() { return it->next(); }
  6131. virtual bool isValid() { return it->isValid(); }
  6132. virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryProp(name)); return s; }
  6133. };
  6134. //==========================================================================================
  6135. CLocalWorkUnit::CLocalWorkUnit(ISecManager *secmgr, ISecUser *secuser)
  6136. {
  6137. clearCached(false);
  6138. secMgr.set(secmgr);
  6139. secUser.set(secuser);
  6140. workflowIteratorCached = false;
  6141. resultsCached = false;
  6142. graphsCached = false;
  6143. temporariesCached = false;
  6144. variablesCached = false;
  6145. exceptionsCached = false;
  6146. pluginsCached = false;
  6147. librariesCached = false;
  6148. activitiesCached = false;
  6149. webServicesInfoCached = false;
  6150. roxieQueryInfoCached = false;
  6151. }
  6152. void CLocalWorkUnit::clearCached(bool clearTree)
  6153. {
  6154. query.clear();
  6155. webServicesInfo.clear();
  6156. workflowIterator.clear();
  6157. graphs.kill();
  6158. results.kill();
  6159. variables.kill();
  6160. plugins.kill();
  6161. libraries.kill();
  6162. exceptions.kill();
  6163. temporaries.kill();
  6164. statistics.kill();
  6165. appvalues.kill();
  6166. if (clearTree)
  6167. p.clear();
  6168. workflowIteratorCached = false;
  6169. resultsCached = false;
  6170. graphsCached = false;
  6171. temporariesCached = false;
  6172. variablesCached = false;
  6173. exceptionsCached = false;
  6174. pluginsCached = false;
  6175. librariesCached = false;
  6176. activitiesCached = false;
  6177. webServicesInfoCached = false;
  6178. roxieQueryInfoCached = false;
  6179. }
  6180. void CLocalWorkUnit::loadPTree(IPropertyTree *ptree)
  6181. {
  6182. clearCached(false);
  6183. p.setown(ptree);
  6184. }
  6185. void CLocalWorkUnit::beforeDispose()
  6186. {
  6187. try
  6188. {
  6189. unsubscribe();
  6190. clearCached(true);
  6191. userDesc.clear();
  6192. secMgr.clear();
  6193. secUser.clear();
  6194. }
  6195. catch (IException *E) { LOG(MCexception(E, MSGCLS_warning), E, "Exception during ~CLocalWorkUnit"); E->Release(); }
  6196. }
  6197. void CLocalWorkUnit::cleanupAndDelete(bool deldll, bool deleteOwned, const StringArray *deleteExclusions)
  6198. {
  6199. MTIME_SECTION(queryActiveTimer(), "WUDELETE cleanupAndDelete total");
  6200. // Delete any related things in SDS etc that might otherwise be forgotten
  6201. if (p->getPropBool("@protected", false))
  6202. throw MakeStringException(WUERR_WorkunitProtected, "%s: Workunit is protected",p->queryName());
  6203. switch (getState())
  6204. {
  6205. case WUStateAborted:
  6206. case WUStateCompleted:
  6207. case WUStateFailed:
  6208. case WUStateArchived:
  6209. break;
  6210. case WUStateCompiled:
  6211. if (getAction()==WUActionRun || getAction()==WUActionUnknown)
  6212. throw MakeStringException(WUERR_WorkunitActive, "%s: Workunit is active. Please abort before deleting this workunit.",p->queryName());
  6213. break;
  6214. case WUStateWait:
  6215. throw MakeStringException(WUERR_WorkunitScheduled, "%s: Workunit is scheduled",p->queryName());
  6216. default:
  6217. throw MakeStringException(WUERR_WorkunitActive, "%s: Workunit is active. Please abort before deleting this workunit.",p->queryName());
  6218. break;
  6219. }
  6220. if (getIsQueryService())
  6221. {
  6222. Owned<IPropertyTree> registry = getQueryRegistryRoot();
  6223. if (registry)
  6224. {
  6225. VStringBuffer xpath("QuerySet/Query[@wuid='%s']", p->queryName());
  6226. if (registry->hasProp(xpath.str()))
  6227. throw MakeStringException(WUERR_WorkunitPublished, "%s: Workunit is published",p->queryName());
  6228. }
  6229. }
  6230. try
  6231. {
  6232. if (deldll && !p->getPropBool("@isClone", false))
  6233. {
  6234. Owned<IConstWUQuery> q = getQuery();
  6235. if (q)
  6236. {
  6237. Owned<IConstWUAssociatedFileIterator> iter = &q->getAssociatedFiles();
  6238. SCMStringBuffer name;
  6239. ForEach(*iter)
  6240. {
  6241. IConstWUAssociatedFile & cur = iter->query();
  6242. cur.getNameTail(name);
  6243. if (!deleteExclusions || (NotFound == deleteExclusions->find(name.str())))
  6244. {
  6245. Owned<IDllEntry> entry = queryDllServer().getEntry(name.str());
  6246. if (entry.get())
  6247. asyncRemoveDll(name.str());
  6248. else
  6249. {
  6250. SCMStringBuffer ip, localPath;
  6251. cur.getName(localPath);
  6252. cur.getIp(ip);
  6253. asyncRemoveFile(ip.str(), localPath.str());
  6254. }
  6255. }
  6256. }
  6257. }
  6258. }
  6259. globalFactory->clearAborting(queryWuid());
  6260. deleteTempFiles(NULL, deleteOwned, true); // all, any remaining.
  6261. }
  6262. catch(IException *E)
  6263. {
  6264. StringBuffer s;
  6265. LOG(MCexception(E, MSGCLS_warning), E, s.append("Exception during cleanupAndDelete: ").append(p->queryName()).str());
  6266. E->Release();
  6267. }
  6268. catch (...)
  6269. {
  6270. WARNLOG("Unknown exception during cleanupAndDelete: %s", p->queryName());
  6271. }
  6272. }
  6273. void CLocalWorkUnit::setTimeScheduled(const IJlibDateTime &val)
  6274. {
  6275. SCMStringBuffer strval;
  6276. val.getGmtString(strval);
  6277. p->setProp("@timescheduled",strval.str());
  6278. }
  6279. IJlibDateTime & CLocalWorkUnit::getTimeScheduled(IJlibDateTime &val) const
  6280. {
  6281. StringBuffer str;
  6282. p->getProp("@timescheduled",str);
  6283. if(str.length())
  6284. val.setGmtString(str.str());
  6285. return val;
  6286. }
  6287. bool modifyAndWriteWorkUnitXML(char const * wuid, StringBuffer & buf, StringBuffer & extra, IFileIO * fileio)
  6288. {
  6289. // kludge in extra chunks of XML such as GraphProgress and GeneratedDlls
  6290. if (extra.length())
  6291. {
  6292. const char *bufStart = buf.str();
  6293. const char *bufPtr = bufStart+buf.length();
  6294. while (bufStart != bufPtr)
  6295. {
  6296. --bufPtr;
  6297. if (!isspace(*bufPtr))
  6298. break;
  6299. }
  6300. assertex('>' == *bufPtr);
  6301. size_t l = strlen(wuid);
  6302. assertex((size_t)(bufPtr-bufStart) > l+2); // e.g. at least </W20171111-111111>
  6303. bufPtr -= l+2; // skip back over </wuid
  6304. assertex(0 == memcmp(bufPtr, "</", 2) );
  6305. assertex(0 == memcmp(bufPtr+2, wuid, l));
  6306. buf.insert(bufPtr-bufStart, extra);
  6307. }
  6308. return (fileio->write(0,buf.length(),buf.str()) == buf.length());
  6309. }
  6310. bool CLocalWorkUnit::archiveWorkUnit(const char *base, bool del, bool ignoredllerrors, bool deleteOwned, bool exportAssociatedFiles)
  6311. {
  6312. CriticalBlock block(crit);
  6313. StringBuffer path(base);
  6314. if (!p)
  6315. return false;
  6316. const char *wuid = p->queryName();
  6317. if (!wuid||!*wuid)
  6318. return false;
  6319. addPathSepChar(path).append(wuid).append(".xml");
  6320. Owned<IFile> file = createIFile(path.str());
  6321. if (!file)
  6322. return false;
  6323. Owned<IFileIO> fileio = file->open(IFOcreate);
  6324. if (!fileio)
  6325. return false;
  6326. StringBuffer buf;
  6327. exportWorkUnitToXML(this, buf, false, false, true);
  6328. StringBuffer extraWorkUnitXML;
  6329. Owned<IPTree> graphProgress = getGraphProgressTree();
  6330. if (graphProgress)
  6331. {
  6332. toXML(graphProgress,extraWorkUnitXML,1,XML_Format);
  6333. graphProgress.clear();
  6334. }
  6335. Owned<IConstWUQuery> q = getQuery();
  6336. if (!q)
  6337. {
  6338. if (!modifyAndWriteWorkUnitXML(wuid, buf, extraWorkUnitXML, fileio))
  6339. return false;
  6340. if (del)
  6341. {
  6342. if (getState()==WUStateUnknown)
  6343. setState(WUStateArchived); // to allow delete
  6344. cleanupAndDelete(false,deleteOwned); // no query, may as well delete
  6345. }
  6346. return false;
  6347. }
  6348. StringArray deleteExclusions; // associated files not to delete, added if failure to copy
  6349. Owned<IConstWUAssociatedFileIterator> iter = &q->getAssociatedFiles();
  6350. Owned<IPropertyTree> generatedDlls = createPTree("GeneratedDlls");
  6351. ForEach(*iter)
  6352. {
  6353. IConstWUAssociatedFile & cur = iter->query();
  6354. SCMStringBuffer name;
  6355. cur.getNameTail(name);
  6356. if (name.length())
  6357. {
  6358. Owned<IDllEntry> entry = queryDllServer().getEntry(name.str());
  6359. SCMStringBuffer curPath, curIp;
  6360. cur.getName(curPath);
  6361. cur.getIp(curIp);
  6362. SocketEndpoint curEp(curIp.str());
  6363. RemoteFilename curRfn;
  6364. curRfn.setPath(curEp, curPath.str());
  6365. StringBuffer dst(base);
  6366. addPathSepChar(dst);
  6367. curRfn.getTail(dst);
  6368. Owned<IFile> dstFile = createIFile(dst.str());
  6369. if (entry.get())
  6370. {
  6371. Owned<IException> exception;
  6372. Owned<IDllLocation> loc;
  6373. Owned<IPropertyTree> generatedDllBranch = createPTree();
  6374. generatedDllBranch->setProp("@name", entry->queryName());
  6375. generatedDllBranch->setProp("@kind", entry->queryKind());
  6376. if (exportAssociatedFiles)
  6377. {
  6378. try
  6379. {
  6380. loc.setown(entry->getBestLocation()); //throws exception if no readable locations
  6381. }
  6382. catch(IException * e)
  6383. {
  6384. exception.setown(e);
  6385. loc.setown(entry->getBestLocationCandidate()); //this will be closest of the unreadable locations
  6386. }
  6387. RemoteFilename filename;
  6388. loc->getDllFilename(filename);
  6389. if (!exception)
  6390. {
  6391. Owned<IFile> srcfile = createIFile(filename);
  6392. try
  6393. {
  6394. if (dstFile->exists())
  6395. {
  6396. if (streq(srcfile->queryFilename(), dstFile->queryFilename()))
  6397. deleteExclusions.append(name.str()); // restored workunit, referencing archive location for query dll (no longer true post HPCC-11191 fix)
  6398. // still want to delete if already archived but there are source file copies
  6399. }
  6400. else
  6401. copyFile(dstFile, srcfile);
  6402. }
  6403. catch(IException * e)
  6404. {
  6405. exception.setown(e);
  6406. }
  6407. }
  6408. if (exception)
  6409. {
  6410. if (ignoredllerrors)
  6411. {
  6412. EXCLOG(exception.get(), "archiveWorkUnit (copying associated file)");
  6413. //copy failed, so don't delete the registered dll files
  6414. deleteExclusions.append(name.str());
  6415. }
  6416. else
  6417. throw exception.getClear();
  6418. }
  6419. }
  6420. // Record Associated path to restore back to
  6421. StringBuffer restorePath;
  6422. curRfn.getRemotePath(restorePath);
  6423. generatedDllBranch->setProp("@location", restorePath.str());
  6424. generatedDlls->addPropTree("GeneratedDll", generatedDllBranch.getClear());
  6425. }
  6426. else if (exportAssociatedFiles) // no generated dll entry
  6427. {
  6428. Owned<IFile> srcFile = createIFile(curRfn);
  6429. try
  6430. {
  6431. copyFile(dstFile, srcFile);
  6432. }
  6433. catch (IException *e)
  6434. {
  6435. VStringBuffer msg("Failed to archive associated file '%s' to destination '%s'", srcFile->queryFilename(), dstFile->queryFilename());
  6436. EXCLOG(e, msg.str());
  6437. e->Release();
  6438. deleteExclusions.append(name.str());
  6439. }
  6440. }
  6441. }
  6442. }
  6443. iter.clear();
  6444. if (generatedDlls->numChildren())
  6445. toXML(generatedDlls, extraWorkUnitXML, 1, XML_Format);
  6446. if (!modifyAndWriteWorkUnitXML(wuid, buf, extraWorkUnitXML, fileio))
  6447. return false;
  6448. if (del)
  6449. {
  6450. //setState(WUStateArchived); // this isn't useful as about to delete it!
  6451. q.clear();
  6452. cleanupAndDelete(true, deleteOwned, &deleteExclusions);
  6453. }
  6454. return true;
  6455. }
  6456. void CLocalWorkUnit::loadXML(const char *xml)
  6457. {
  6458. CriticalBlock block(crit);
  6459. clearCached(true);
  6460. assertex(xml);
  6461. p.setown(createPTreeFromXMLString(xml,ipt_lowmem));
  6462. }
  6463. void CLocalWorkUnit::serialize(MemoryBuffer &tgt)
  6464. {
  6465. CriticalBlock block(crit);
  6466. StringBuffer x;
  6467. tgt.append(exportWorkUnitToXML(this, x, false, false, false).str());
  6468. }
  6469. void CLocalWorkUnit::deserialize(MemoryBuffer &src)
  6470. {
  6471. CriticalBlock block(crit);
  6472. StringAttr value;
  6473. src.read(value);
  6474. loadXML(value);
  6475. }
  6476. void CLocalWorkUnit::requestAbort()
  6477. {
  6478. CriticalBlock block(crit);
  6479. abortWorkUnit(p->queryName());
  6480. }
  6481. void CLocalWorkUnit::unlockRemote()
  6482. {
  6483. CriticalBlock block(crit);
  6484. locked.unlock();
  6485. _unlockRemote();
  6486. }
  6487. IWorkUnit &CLocalWorkUnit::lockRemote(bool commit)
  6488. {
  6489. if (secMgr)
  6490. checkWuSecAccess(*this, secMgr.get(), secUser.get(), SecAccess_Write, "write lock", true, true);
  6491. locked.lock();
  6492. CriticalBlock block(crit);
  6493. if (commit)
  6494. {
  6495. try
  6496. {
  6497. _lockRemote();
  6498. }
  6499. catch (IException *E)
  6500. {
  6501. StringBuffer s;
  6502. IERRLOG("Failed to get write lock on workunit: %s", E->errorMessage(s).str());
  6503. locked.unlock();
  6504. throw;
  6505. }
  6506. }
  6507. return *new CLockedWorkUnit(LINK(this));
  6508. }
  6509. void CLocalWorkUnit::commit()
  6510. {
  6511. // Nothing to do if not backed by a persistent store
  6512. }
  6513. IWorkUnit& CLocalWorkUnit::lock()
  6514. {
  6515. return lockRemote(true);
  6516. }
  6517. IConstWorkUnit *CLocalWorkUnit::unlock()
  6518. {
  6519. unlockRemote();
  6520. return this;
  6521. }
  6522. const char *CLocalWorkUnit::queryWuid() const
  6523. {
  6524. CriticalBlock block(crit);
  6525. return p->queryName();
  6526. }
  6527. unsigned CLocalWorkUnit::getDebugAgentListenerPort() const
  6528. {
  6529. CriticalBlock block(crit);
  6530. return p->getPropInt("@DebugListenerPort", 0);
  6531. }
  6532. unsigned CLocalWorkUnit::getTotalThorTime() const
  6533. {
  6534. CriticalBlock block(crit);
  6535. if (p->hasProp("@totalThorTime"))
  6536. return (unsigned)nanoToMilli(extractTimeCollatable(p->queryProp("@totalThorTime"), nullptr));
  6537. const WuScopeFilter filter("stype[graph],nested[0],stat[TimeElapsed]");
  6538. StatsAggregation summary;
  6539. aggregateStatistic(summary, (IConstWorkUnit *)this, filter, StTimeElapsed);
  6540. return (unsigned)nanoToMilli(summary.getSum());
  6541. }
  6542. void CLocalWorkUnit::setDebugAgentListenerPort(unsigned port)
  6543. {
  6544. CriticalBlock block(crit);
  6545. p->setPropInt("@DebugListenerPort", port);
  6546. }
  6547. IStringVal& CLocalWorkUnit::getDebugAgentListenerIP(IStringVal &ip) const
  6548. {
  6549. CriticalBlock block(crit);
  6550. ip.set(p->queryProp("@DebugListenerIP"));
  6551. return ip;
  6552. }
  6553. void CLocalWorkUnit::setDebugAgentListenerIP(const char * ip)
  6554. {
  6555. CriticalBlock block(crit);
  6556. p->setProp("@DebugListenerIP", ip);
  6557. }
  6558. IStringVal & CLocalWorkUnit::getWorkunitDistributedAccessToken(IStringVal & datoken) const
  6559. {
  6560. CriticalBlock block(crit);
  6561. datoken.set(p->queryProp("@distributedAccessToken"));
  6562. return datoken;
  6563. }
  6564. bool CLocalWorkUnit::setDistributedAccessToken(const char * user)
  6565. {
  6566. CriticalBlock block(crit);
  6567. //If this format changes, you must update the isWorkunitDAToken() and extractFromWorkunitDAToken() methods
  6568. VStringBuffer datoken("HPCC[u=%s,w=%s]", user, queryWuid());
  6569. IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
  6570. if (pDSM && pDSM->isDigiSignerConfigured())
  6571. {
  6572. datoken.append(pDSM->queryKeyName()).append(';');
  6573. StringBuffer b64Signature;
  6574. if (pDSM->digiSign(b64Signature, datoken))
  6575. {
  6576. datoken.append(b64Signature.str());
  6577. }
  6578. else
  6579. {
  6580. ERRLOG("Cannot create workunit Distributed Access Token, digisign failed");
  6581. return false;
  6582. }
  6583. }
  6584. else
  6585. {
  6586. if (workUnitTraceLevel > 1)
  6587. WARNLOG("Cannot sign Distributed Access Token, digisign signing not configured");
  6588. datoken.append(";");
  6589. }
  6590. p->setProp("@distributedAccessToken", datoken);
  6591. return true;
  6592. }
  6593. bool CLocalWorkUnit::getRunningGraph(IStringVal &graphName, WUGraphIDType &subId) const
  6594. {
  6595. // Only implemented in derived classes
  6596. return false;
  6597. }
  6598. void CLocalWorkUnit::setJobName(const char *value)
  6599. {
  6600. CriticalBlock block(crit);
  6601. p->setProp("@jobName", value);
  6602. }
  6603. const char *CLocalWorkUnit::queryJobName() const
  6604. {
  6605. CriticalBlock block(crit);
  6606. const char *ret = p->queryProp("@jobName");
  6607. if (!ret)
  6608. ret = "";
  6609. return ret;
  6610. }
  6611. void CLocalWorkUnit::setClusterName(const char *value)
  6612. {
  6613. CriticalBlock block(crit);
  6614. p->setProp("@clusterName", value);
  6615. }
  6616. const char *CLocalWorkUnit::queryClusterName() const
  6617. {
  6618. CriticalBlock block(crit);
  6619. const char *ret = p->queryProp("@clusterName");
  6620. if (!ret)
  6621. ret = "";
  6622. return ret;
  6623. }
  6624. void CLocalWorkUnit::setAllowedClusters(const char *value)
  6625. {
  6626. setDebugValue("allowedclusters",value, true);
  6627. }
  6628. IStringVal& CLocalWorkUnit::getAllowedClusters(IStringVal &str) const
  6629. {
  6630. CriticalBlock block(crit);
  6631. getDebugValue("allowedclusters",str);
  6632. if (str.length()!=0)
  6633. return str;
  6634. str.set(p->queryProp("@clusterName"));
  6635. return str;
  6636. }
  6637. void CLocalWorkUnit::setAllowAutoQueueSwitch(bool val)
  6638. {
  6639. setDebugValueInt("allowautoqueueswitch",val?1:0,true);
  6640. }
  6641. bool CLocalWorkUnit::getAllowAutoQueueSwitch() const
  6642. {
  6643. CriticalBlock block(crit);
  6644. return getDebugValueBool("allowautoqueueswitch",false);
  6645. }
  6646. void CLocalWorkUnit::setLibraryInformation(const char * name, unsigned interfaceHash, unsigned definitionHash)
  6647. {
  6648. StringBuffer suffix;
  6649. if (name && *name)
  6650. setApplicationValue("LibraryModule", "name", name, true);
  6651. setApplicationValueInt("LibraryModule", "interfaceHash", interfaceHash, true);
  6652. setApplicationValueInt("LibraryModule", "definitionHash", definitionHash, true);
  6653. setApplicationValue("LibraryModule", "platform", appendLibrarySuffix(suffix).str(), true);
  6654. }
  6655. void CLocalWorkUnit::remoteCheckAccess(IUserDescriptor *user, bool writeaccess) const
  6656. {
  6657. unsigned auditflags = DALI_LDAP_AUDIT_REPORT|DALI_LDAP_READ_WANTED;
  6658. if (writeaccess)
  6659. auditflags |= DALI_LDAP_WRITE_WANTED;
  6660. SecAccessFlags perm = SecAccess_Full;
  6661. const char *scopename = p->queryProp("@scope");
  6662. if (scopename&&*scopename) {
  6663. if (!user)
  6664. user = queryUserDescriptor();
  6665. perm = querySessionManager().getPermissionsLDAP("workunit",scopename,user,auditflags);
  6666. if (perm<0) {
  6667. if (perm == SecAccess_Unavailable)
  6668. perm = SecAccess_Full;
  6669. else
  6670. perm = SecAccess_None;
  6671. }
  6672. }
  6673. if (!HASREADPERMISSION(perm))
  6674. throw MakeStringException(WUERR_WorkunitAccessDenied, "Read access denied for workunit %s", queryWuid());
  6675. if (writeaccess && !HASWRITEPERMISSION(perm))
  6676. throw MakeStringException(WUERR_WorkunitAccessDenied, "Write access denied for workunit %s", queryWuid());
  6677. }
  6678. void CLocalWorkUnit::setUser(const char * value)
  6679. {
  6680. CriticalBlock block(crit);
  6681. p->setProp("@submitID", value);
  6682. }
  6683. const char *CLocalWorkUnit::queryUser() const
  6684. {
  6685. CriticalBlock block(crit);
  6686. const char *ret = p->queryProp("@submitID");
  6687. if (!ret)
  6688. ret = "";
  6689. return ret;
  6690. }
  6691. void CLocalWorkUnit::setWuScope(const char * value)
  6692. {
  6693. if (value && *value)
  6694. {
  6695. if (checkWuScopeSecAccess(value, secMgr.get(), secUser.get(), SecAccess_Write, "Change Scope", true, true))
  6696. {
  6697. CriticalBlock block(crit);
  6698. p->setProp("@scope", value);
  6699. }
  6700. }
  6701. }
  6702. const char *CLocalWorkUnit::queryWuScope() const
  6703. {
  6704. CriticalBlock block(crit);
  6705. const char *ret = p->queryProp("@scope");
  6706. if (!ret)
  6707. ret = "";
  6708. return ret;
  6709. }
  6710. void CLocalWorkUnit::setPriority(WUPriorityClass cls)
  6711. {
  6712. CriticalBlock block(crit);
  6713. setEnum(p, "@priorityClass", cls, priorityClasses);
  6714. }
  6715. WUPriorityClass CLocalWorkUnit::getPriority() const
  6716. {
  6717. CriticalBlock block(crit);
  6718. return (WUPriorityClass) getEnum(p, "@priorityClass", priorityClasses);
  6719. }
  6720. const char *CLocalWorkUnit::queryPriorityDesc() const
  6721. {
  6722. return getEnumText(getPriority(), priorityClasses);
  6723. }
  6724. void CLocalWorkUnit::setState(WUState value)
  6725. {
  6726. if (value==WUStateAborted || value==WUStatePaused || value==WUStateCompleted || value==WUStateFailed || value==WUStateSubmitted || value==WUStateWait)
  6727. {
  6728. if (globalFactory)
  6729. globalFactory->clearAborting(queryWuid());
  6730. }
  6731. CriticalBlock block(crit);
  6732. setEnum(p, "@state", value, states); // For historical reasons, we use state to store the state
  6733. setEnum(p, "State", value, states); // But we can only subscribe to elements, not attributes
  6734. if (getDebugValueBool("monitorWorkunit", false))
  6735. {
  6736. switch(value)
  6737. {
  6738. case WUStateAborted:
  6739. FLLOG(MCoperatorWarning, "Workunit %s aborted", p->queryName());
  6740. break;
  6741. case WUStateCompleted:
  6742. FLLOG(MCoperatorProgress, "Workunit %s completed", p->queryName());
  6743. break;
  6744. case WUStateFailed:
  6745. FLLOG(MCoperatorProgress, "Workunit %s failed", p->queryName());
  6746. break;
  6747. }
  6748. }
  6749. p->removeProp("@stateEx");
  6750. }
  6751. void CLocalWorkUnit::setStateEx(const char * text)
  6752. {
  6753. CriticalBlock block(crit);
  6754. p->setProp("@stateEx", text);
  6755. }
  6756. void CLocalWorkUnit::setAgentSession(__int64 sessionId)
  6757. {
  6758. CriticalBlock block(crit);
  6759. p->setPropInt64("@agentSession", sessionId);
  6760. }
  6761. bool CLocalWorkUnit::getIsQueryService() const
  6762. {
  6763. CriticalBlock block(crit);
  6764. return p->getPropBool("@isQueryService", false);
  6765. }
  6766. void CLocalWorkUnit::setIsQueryService(bool value)
  6767. {
  6768. CriticalBlock block(crit);
  6769. p->setPropBool("@isQueryService", value);
  6770. }
  6771. void CLocalWorkUnit::checkAgentRunning(WUState & state)
  6772. {
  6773. if (queryDaliServerVersion().compare("2.1")<0)
  6774. return;
  6775. switch(state)
  6776. {
  6777. case WUStateRunning:
  6778. case WUStateDebugPaused:
  6779. case WUStateDebugRunning:
  6780. case WUStateBlocked:
  6781. case WUStateAborting:
  6782. case WUStateCompiling:
  6783. case WUStatePaused:
  6784. {
  6785. SessionId agent = getAgentSession();
  6786. if((agent>0) && querySessionManager().sessionStopped(agent, 0))
  6787. {
  6788. forceReload();
  6789. state = (WUState) getEnum(p, "@state", states);
  6790. bool isecl=state==WUStateCompiling;
  6791. if (aborting())
  6792. state = WUStateAborted;
  6793. else if (state==WUStateRunning || state==WUStatePaused || state==WUStateDebugPaused || state==WUStateDebugRunning || state==WUStateBlocked || state==WUStateCompiling)
  6794. state = WUStateFailed;
  6795. else
  6796. return;
  6797. WARNLOG("checkAgentRunning terminated: %" I64F "d state = %d",(__int64)agent,(int)state);
  6798. Owned<IWorkUnit> w = &lock();
  6799. w->setState(state);
  6800. Owned<IWUException> e = w->createException();
  6801. WUAction action = w->getAction();
  6802. switch (action)
  6803. {
  6804. case WUActionPause:
  6805. case WUActionPauseNow:
  6806. case WUActionResume:
  6807. w->setAction(WUActionUnknown);
  6808. }
  6809. if(isecl)
  6810. {
  6811. e->setExceptionCode(1001);
  6812. e->setExceptionMessage("EclServer terminated unexpectedly");
  6813. }
  6814. else
  6815. {
  6816. e->setExceptionCode(1000);
  6817. e->setExceptionMessage("Workunit terminated unexpectedly");
  6818. }
  6819. }
  6820. }
  6821. }
  6822. }
  6823. WUState CLocalWorkUnit::getState() const
  6824. {
  6825. CriticalBlock block(crit);
  6826. WUState state = (WUState) getEnum(p, "@state", states);
  6827. switch (state)
  6828. {
  6829. case WUStateRunning:
  6830. case WUStateDebugPaused:
  6831. case WUStateDebugRunning:
  6832. case WUStateBlocked:
  6833. case WUStateCompiling:
  6834. if (aborting())
  6835. state = WUStateAborting;
  6836. break;
  6837. case WUStateSubmitted:
  6838. if (aborting())
  6839. state = WUStateAborted;
  6840. break;
  6841. }
  6842. const_cast<CLocalWorkUnit *>(this)->checkAgentRunning(state); //need const_cast as will change state if agent has died
  6843. return state;
  6844. }
  6845. IStringVal& CLocalWorkUnit::getStateEx(IStringVal & str) const
  6846. {
  6847. CriticalBlock block(crit);
  6848. str.set(p->queryProp("@stateEx"));
  6849. return str;
  6850. }
  6851. __int64 CLocalWorkUnit::getAgentSession() const
  6852. {
  6853. CriticalBlock block(crit);
  6854. return p->getPropInt64("@agentSession", -1);
  6855. }
  6856. unsigned CLocalWorkUnit::getAgentPID() const
  6857. {
  6858. CriticalBlock block(crit);
  6859. return p->getPropInt("@agentPID", -1);
  6860. }
  6861. const char * CLocalWorkUnit::queryStateDesc() const
  6862. {
  6863. // MORE - not sure about this - may prefer a separate interface
  6864. CriticalBlock block(crit);
  6865. try
  6866. {
  6867. return getEnumText(getState(), states);
  6868. }
  6869. catch (...)
  6870. {
  6871. return "???";
  6872. }
  6873. }
  6874. void CLocalWorkUnit::setAction(WUAction value)
  6875. {
  6876. CriticalBlock block(crit);
  6877. setEnum(p, "Action", value, actions);
  6878. }
  6879. WUAction CLocalWorkUnit::getAction() const
  6880. {
  6881. CriticalBlock block(crit);
  6882. return (WUAction) getEnum(p, "Action", actions);
  6883. }
  6884. const char *CLocalWorkUnit::queryActionDesc() const
  6885. {
  6886. CriticalBlock block(crit);
  6887. return p->queryProp("Action");
  6888. }
  6889. bool CLocalWorkUnit::hasApplicationValue(const char *app, const char *propname) const
  6890. {
  6891. StringBuffer prop("Application/");
  6892. prop.append(app).append('/').append(propname);
  6893. CriticalBlock block(crit);
  6894. return p->hasProp(prop);
  6895. }
  6896. IStringVal& CLocalWorkUnit::getApplicationValue(const char *app, const char *propname, IStringVal &str) const
  6897. {
  6898. StringBuffer prop("Application/");
  6899. prop.append(app).append('/').append(propname);
  6900. CriticalBlock block(crit);
  6901. str.set(p->queryProp(prop.str()));
  6902. return str;
  6903. }
  6904. int CLocalWorkUnit::getApplicationValueInt(const char *app, const char *propname, int defVal) const
  6905. {
  6906. StringBuffer prop("Application/");
  6907. prop.append(app).append('/').append(propname);
  6908. CriticalBlock block(crit);
  6909. return p->getPropInt(prop.str(), defVal);
  6910. }
  6911. IConstWUAppValueIterator& CLocalWorkUnit::getApplicationValues() const
  6912. {
  6913. CriticalBlock block(crit);
  6914. appvalues.load(p,"Application/*");
  6915. return *new CArrayIteratorOf<IConstWUAppValue,IConstWUAppValueIterator> (appvalues, 0, (IConstWorkUnit *) this);
  6916. }
  6917. void CLocalWorkUnit::setApplicationValue(const char *app, const char *propname, const char *value, bool overwrite)
  6918. {
  6919. CriticalBlock block(crit);
  6920. StringBuffer prop("Application/");
  6921. prop.append(app).append('/').append(propname);
  6922. if (overwrite || !p->hasProp(prop.str()))
  6923. {
  6924. StringBuffer sp;
  6925. p->setProp(sp.append("Application").str(), "");
  6926. p->setProp(sp.append('/').append(app).str(), "");
  6927. p->setProp(prop.str(), value);
  6928. }
  6929. }
  6930. void CLocalWorkUnit::setApplicationValueInt(const char *app, const char *propname, int value, bool overwrite)
  6931. {
  6932. VStringBuffer s("%d", value);
  6933. setApplicationValue(app, propname, s, overwrite);
  6934. }
  6935. void CLocalWorkUnit::setPriorityLevel(int level)
  6936. {
  6937. CriticalBlock block(crit);
  6938. p->setPropInt("PriorityFlag", level);
  6939. }
  6940. int CLocalWorkUnit::getPriorityLevel() const
  6941. {
  6942. CriticalBlock block(crit);
  6943. return p->getPropInt("PriorityFlag");
  6944. }
  6945. int calcPriorityValue(const IPropertyTree * p)
  6946. {
  6947. int priority = p->getPropInt("PriorityFlag");
  6948. switch((WUPriorityClass) getEnum(p, "@priorityClass", priorityClasses))
  6949. {
  6950. case PriorityClassLow:
  6951. priority -= 100;
  6952. break;
  6953. case PriorityClassHigh:
  6954. priority += 100;
  6955. break;
  6956. }
  6957. return priority;
  6958. }
  6959. int CLocalWorkUnit::getPriorityValue() const
  6960. {
  6961. CriticalBlock block(crit);
  6962. return calcPriorityValue(p);
  6963. }
  6964. void CLocalWorkUnit::setRescheduleFlag(bool value)
  6965. {
  6966. CriticalBlock block(crit);
  6967. p->setPropInt("RescheduleFlag", (int) value);
  6968. }
  6969. bool CLocalWorkUnit::getRescheduleFlag() const
  6970. {
  6971. CriticalBlock block(crit);
  6972. return p->getPropInt("RescheduleFlag") != 0;
  6973. }
  6974. class NullIStringIterator : implements IStringIterator, public CInterface
  6975. {
  6976. public:
  6977. IMPLEMENT_IINTERFACE;
  6978. bool first() { return false; }
  6979. bool next() { return false; }
  6980. bool isValid() { return false; }
  6981. IStringVal & str(IStringVal & str) { return str; }
  6982. };
  6983. ClusterType getClusterType(const char * platform, ClusterType dft)
  6984. {
  6985. if (stricmp(platform, "thor") == 0)
  6986. return ThorLCRCluster;
  6987. if (stricmp(platform, "thorlcr") == 0)
  6988. return ThorLCRCluster;
  6989. if (stricmp(platform, "hthor") == 0)
  6990. return HThorCluster;
  6991. if (stricmp(platform, "roxie") == 0)
  6992. return RoxieCluster;
  6993. return dft;
  6994. }
  6995. const char *clusterTypeString(ClusterType clusterType, bool lcrSensitive)
  6996. {
  6997. switch (clusterType)
  6998. {
  6999. case ThorLCRCluster:
  7000. if (lcrSensitive)
  7001. return "thorlcr";
  7002. return "thor";
  7003. case RoxieCluster:
  7004. return "roxie";
  7005. case HThorCluster:
  7006. return "hthor";
  7007. }
  7008. throwUnexpected();
  7009. }
  7010. bool extractFromWorkunitDAToken(const char * distributedAccessToken, StringBuffer * wuid, StringBuffer * user, StringBuffer * privKey)
  7011. {
  7012. if (!isWorkunitDAToken(distributedAccessToken))
  7013. {
  7014. DBGLOG("Not a valid workunit distributed access token");
  7015. return false;
  7016. }
  7017. //Extract the string between [ and ]
  7018. const char * pEndBracket = strchr(distributedAccessToken + 5, ']');
  7019. StringBuffer tokenValues;
  7020. tokenValues.append(pEndBracket - distributedAccessToken - 5, distributedAccessToken + 5);
  7021. StringArray nameValues;
  7022. nameValues.appendList(tokenValues.str(), ",",true);
  7023. if (user || wuid)
  7024. {
  7025. for (int x = 0; x < nameValues.ordinality(); x++)
  7026. {
  7027. if (user && 0==strncmp(nameValues[x],"u=",2))//Extract user
  7028. user->append(nameValues[x] + 2);
  7029. if (wuid && 0==strncmp(nameValues[x],"w=",2))//Extract wuid
  7030. wuid->append(nameValues[x] + 2);
  7031. }
  7032. }
  7033. if (privKey)
  7034. {
  7035. const char * finger = pEndBracket;
  7036. ++finger;
  7037. while (*finger && *finger != ';')
  7038. privKey->append(1, finger++);
  7039. }
  7040. return true;
  7041. }
  7042. //Verifies the given signed workunit distributed access token was created
  7043. //and signed by the given wuid and user, and that the workunit is still active
  7044. //Returns:
  7045. // 0 : Success, token is valid and workunit is active
  7046. // 1 : Signature does not verify (wuid/username don't match, or signature does not verify)
  7047. // 2 : Workunit not active
  7048. // Throws if unable to open workunit
  7049. wuTokenStates verifyWorkunitDAToken(const char * ctxUser, const char * daToken)
  7050. {
  7051. #ifdef _CONTAINERIZED
  7052. if (!getComponentConfigSP()->getPropBool("@wuTokens", false))
  7053. return wuTokenInvalid;
  7054. #endif
  7055. if (isEmptyString(daToken))
  7056. {
  7057. ERRLOG("verifyWorkunitDAToken : Token must be provided");
  7058. return wuTokenInvalid;
  7059. }
  7060. StringBuffer tokWuid;
  7061. StringBuffer tokUser;
  7062. if (!extractFromWorkunitDAToken(daToken, &tokWuid, &tokUser, nullptr))//get the wuid and user
  7063. {
  7064. //Not a valid workunit distributed access token
  7065. return wuTokenInvalid;
  7066. }
  7067. //Validate signature
  7068. IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
  7069. if (pDSM && pDSM->isDigiVerifierConfigured())
  7070. {
  7071. const char * finger;
  7072. StringBuffer token;//receives copy of everything up until signature
  7073. for (finger = daToken; *finger && *finger != ';'; finger++)
  7074. token.append(1, finger);
  7075. token.append(1, finger);//append ;
  7076. StringBuffer sig(++finger);
  7077. if (!pDSM->digiVerify(sig, token))
  7078. {
  7079. ERRLOG("verifyWorkunitDAToken : workunit distributed access token does not verify");
  7080. return wuTokenInvalid;
  7081. }
  7082. }
  7083. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  7084. Owned<IConstWorkUnit> cw = factory->openWorkUnit(tokWuid.str());
  7085. if(!cw)
  7086. {
  7087. throw MakeStringException(WUERR_WorkunitAccessDenied,"verifyWorkunitDAToken : Cannot open workunit %s",tokWuid.str());
  7088. }
  7089. //Verify user matches
  7090. bool wuUserExist = !isEmptyString(cw->queryUser());
  7091. bool tokUserExist = !isEmptyString(tokUser.str());
  7092. bool ctxUserExist = !isEmptyString(ctxUser);
  7093. if (wuUserExist && tokUserExist)
  7094. {
  7095. //if both users are found, they must match
  7096. if (!streq(tokUser.str(), cw->queryUser()))
  7097. {
  7098. ERRLOG("verifyWorkunitDAToken : Token user (%s) does not match WU user (%s)", tokUser.str(), cw->queryUser());
  7099. return wuTokenInvalid;//Possible Internal error
  7100. }
  7101. else if (ctxUserExist && !streq(tokUser.str(), ctxUser))//ctxUser will be empty if security not enabled
  7102. {
  7103. ERRLOG("verifyWorkunitDAToken : Token user (%s) does not match Context user (%s)", cw->queryUser(), ctxUser);
  7104. return wuTokenInvalid;
  7105. }
  7106. }
  7107. else if (!wuUserExist && !tokUserExist)//both users will be empty if no security enabled
  7108. {
  7109. if (ctxUserExist)
  7110. {
  7111. ERRLOG("verifyWorkunitDAToken : Security enabled but WU user and Token user not specified");
  7112. return wuTokenInvalid;
  7113. }
  7114. //both users empty and no context user means if no security enabled
  7115. }
  7116. else
  7117. {
  7118. //one user found, but not the other, treat as an error
  7119. ERRLOG("verifyWorkunitDAToken : WU user %s and Token user %s must be provided", wuUserExist ? cw->queryUser() : "(NULL)", tokUserExist ? tokUser.str() : "(NULL)");
  7120. return wuTokenInvalid;
  7121. }
  7122. // no need to compare tokWuid with workunit wuid, because it will always match
  7123. bool wuActive;
  7124. switch (cw->getState())
  7125. {
  7126. case WUStateRunning:
  7127. case WUStateDebugRunning:
  7128. case WUStateBlocked:
  7129. case WUStateAborting:
  7130. case WUStateUploadingFiles:
  7131. #ifdef _DEBUG
  7132. DBGLOG("verifyWorkunitDAToken : Workunit token validated for %s %s, state is '%s'", cw->queryWuid(), cw->queryUser(), getWorkunitStateStr(cw->getState()));
  7133. #endif
  7134. wuActive = true;
  7135. break;
  7136. default:
  7137. ERRLOG("verifyWorkunitDAToken : Workunit %s not active, state is '%s'", cw->queryWuid(), getWorkunitStateStr(cw->getState()));
  7138. wuActive = false;
  7139. break;
  7140. }
  7141. return wuActive ? wuTokenValid : wuTokenWorkunitInactive;
  7142. }
  7143. bool CLocalWorkUnit::resolveFilePrefix(StringBuffer & prefix, const char * queue) const
  7144. {
  7145. if (hasApplicationValue("prefix", queue))
  7146. {
  7147. getApplicationValue("prefix", queue, StringBufferAdaptor(prefix));
  7148. return true;
  7149. }
  7150. #ifndef _CONTAINERIZED
  7151. Owned<IConstWUClusterInfo> ci = getTargetClusterInfo(queue);
  7152. if (ci)
  7153. {
  7154. ci->getScope(StringBufferAdaptor(prefix));
  7155. return true;
  7156. }
  7157. #endif
  7158. return false;
  7159. }
  7160. IStringVal& CLocalWorkUnit::getScope(IStringVal &str) const
  7161. {
  7162. StringBuffer prefix;
  7163. CriticalBlock block(crit);
  7164. if (p->hasProp("Debug/ForceScope"))
  7165. {
  7166. prefix.append(p->queryProp("Debug/ForceScope")).toLowerCase();
  7167. }
  7168. else
  7169. {
  7170. resolveFilePrefix(prefix, p->queryProp("@clusterName"));
  7171. }
  7172. str.set(prefix.str());
  7173. return str;
  7174. }
  7175. //Queries
  7176. void CLocalWorkUnit::setCodeVersion(unsigned codeVersion, const char * buildVersion, const char * eclVersion)
  7177. {
  7178. CriticalBlock block(crit);
  7179. p->setPropInt("@codeVersion", codeVersion);
  7180. p->setProp("@buildVersion", buildVersion);
  7181. p->setProp("@eclVersion", eclVersion);
  7182. }
  7183. unsigned CLocalWorkUnit::getCodeVersion() const
  7184. {
  7185. CriticalBlock block(crit);
  7186. return p->getPropInt("@codeVersion");
  7187. }
  7188. unsigned CLocalWorkUnit::getWuidVersion() const
  7189. {
  7190. CriticalBlock block(crit);
  7191. return p->getPropInt("@wuidVersion");
  7192. }
  7193. void CLocalWorkUnit::getBuildVersion(IStringVal & buildVersion, IStringVal & eclVersion) const
  7194. {
  7195. CriticalBlock block(crit);
  7196. buildVersion.set(p->queryProp("@buildVersion"));
  7197. eclVersion.set(p->queryProp("@eclVersion"));
  7198. }
  7199. void CLocalWorkUnit::setCloneable(bool value)
  7200. {
  7201. CriticalBlock block(crit);
  7202. p->setPropInt("@cloneable", value);
  7203. }
  7204. void CLocalWorkUnit::setIsClone(bool value)
  7205. {
  7206. CriticalBlock block(crit);
  7207. p->setPropInt("@isClone", value);
  7208. }
  7209. bool CLocalWorkUnit::getCloneable() const
  7210. {
  7211. CriticalBlock block(crit);
  7212. return p->getPropBool("@cloneable", false);
  7213. }
  7214. IUserDescriptor *CLocalWorkUnit::queryUserDescriptor() const
  7215. {
  7216. CriticalBlock block(crit);
  7217. if (!userDesc)
  7218. {
  7219. userDesc.setown(createUserDescriptor());
  7220. SCMStringBuffer token;
  7221. getWorkunitDistributedAccessToken(token);
  7222. userDesc->set(queryUser(), token.str());//use token as password
  7223. }
  7224. return userDesc;
  7225. }
  7226. bool CLocalWorkUnit::isProtected() const
  7227. {
  7228. CriticalBlock block(crit);
  7229. return p->getPropBool("@protected", false);
  7230. }
  7231. bool CLocalWorkUnit::isPausing() const
  7232. {
  7233. CriticalBlock block(crit);
  7234. if (WUActionPause == getAction())
  7235. {
  7236. switch (getState())
  7237. {
  7238. case WUStateRunning:
  7239. case WUStateAborting:
  7240. return true;
  7241. }
  7242. }
  7243. return false;
  7244. }
  7245. void CLocalWorkUnit::protect(bool protectMode)
  7246. {
  7247. CriticalBlock block(crit);
  7248. p->setPropBool("@protected", protectMode);
  7249. }
  7250. void CLocalWorkUnit::setResultLimit(unsigned value)
  7251. {
  7252. CriticalBlock block(crit);
  7253. p->setPropInt("resultLimit", value);
  7254. }
  7255. unsigned CLocalWorkUnit::getResultLimit() const
  7256. {
  7257. CriticalBlock block(crit);
  7258. return p->getPropInt("resultLimit");
  7259. }
  7260. IStringVal & CLocalWorkUnit::getSnapshot(IStringVal & str) const
  7261. {
  7262. CriticalBlock block(crit);
  7263. str.set(p->queryProp("SNAPSHOT"));
  7264. return str;
  7265. }
  7266. void CLocalWorkUnit::setSnapshot(const char * val)
  7267. {
  7268. CriticalBlock block(crit);
  7269. p->setProp("SNAPSHOT", val);
  7270. }
  7271. const static EnumMapping warningSeverityMap[] =
  7272. {
  7273. { SeverityInformation, "info" },
  7274. { SeverityWarning, "warning" },
  7275. { SeverityError, "error" },
  7276. { SeverityAlert, "alert" },
  7277. { SeverityIgnore, "ignore" },
  7278. { SeverityFatal, "fatal" },
  7279. { SeverityUnknown, NULL }
  7280. };
  7281. ErrorSeverity CLocalWorkUnit::getWarningSeverity(unsigned code, ErrorSeverity defaultSeverity) const
  7282. {
  7283. StringBuffer xpath;
  7284. xpath.append("OnWarnings/OnWarning[@code='").append(code).append("']");
  7285. CriticalBlock block(crit);
  7286. IPropertyTree * mapping = p->queryPropTree(xpath);
  7287. if (mapping)
  7288. return (ErrorSeverity) getEnum(mapping, "@severity", warningSeverityMap);
  7289. return defaultSeverity;
  7290. }
  7291. void CLocalWorkUnit::setWarningSeverity(unsigned code, ErrorSeverity severity)
  7292. {
  7293. StringBuffer xpath;
  7294. xpath.append("OnWarnings/OnWarning[@code='").append(code).append("']");
  7295. CriticalBlock block(crit);
  7296. IPropertyTree * mapping = p->queryPropTree(xpath);
  7297. if (!mapping)
  7298. {
  7299. IPropertyTree * onWarnings = ensurePTree(p, "OnWarnings");
  7300. mapping = onWarnings->addPropTree("OnWarning");
  7301. mapping->setPropInt("@code", code);
  7302. }
  7303. setEnum(mapping, "@severity", severity, warningSeverityMap);
  7304. }
  7305. static int comparePropTrees(IInterface * const *ll, IInterface * const *rr)
  7306. {
  7307. IPropertyTree *l = (IPropertyTree *) *ll;
  7308. IPropertyTree *r = (IPropertyTree *) *rr;
  7309. return stricmp(l->queryName(), r->queryName());
  7310. };
  7311. unsigned CLocalWorkUnit::calculateHash(unsigned crc)
  7312. {
  7313. // Any other values in the WU that could affect generated code should be crc'ed here
  7314. IPropertyTree *tree = p->queryBranch("Debug");
  7315. if (tree)
  7316. {
  7317. Owned<IPropertyTreeIterator> sub = tree->getElements("*");
  7318. ICopyArrayOf<IPropertyTree> subs;
  7319. for(sub->first(); sub->isValid(); sub->next())
  7320. subs.append(sub->query());
  7321. subs.sort(comparePropTrees);
  7322. ForEachItemIn(idx, subs)
  7323. {
  7324. const char *name = subs.item(idx).queryName();
  7325. const char *val = subs.item(idx).queryProp(NULL);
  7326. crc = crc32(name, (size32_t)strlen(name), crc);
  7327. if (val)
  7328. crc = crc32(val, (size32_t)strlen(val), crc);
  7329. }
  7330. }
  7331. Owned<IConstWUPluginIterator> plugins = &getPlugins();
  7332. for (plugins->first();plugins->isValid();plugins->next())
  7333. {
  7334. IConstWUPlugin &thisplugin = plugins->query();
  7335. SCMStringBuffer version;
  7336. thisplugin.getPluginVersion(version);
  7337. crc = crc32(version.str(), version.length(), crc);
  7338. }
  7339. return crc;
  7340. }
  7341. static void updateProp(IPropertyTree * to, const IPropertyTree * from, const char * xpath)
  7342. {
  7343. if (!to->hasProp(xpath) && from->hasProp(xpath))
  7344. to->setProp(xpath, from->queryProp(xpath));
  7345. }
  7346. static void setProp(IPropertyTree * to, const IPropertyTree * from, const char * xpath)
  7347. {
  7348. if (from->hasProp(xpath))
  7349. to->setProp(xpath, from->queryProp(xpath));
  7350. }
  7351. static void copyTree(IPropertyTree * to, const IPropertyTree * from, const char * xpath)
  7352. {
  7353. IPropertyTree * match = from->getBranch(xpath);
  7354. if (match)
  7355. to->setPropTree(xpath, match);
  7356. }
  7357. IPropertyTree *CLocalWorkUnit::queryPTree() const
  7358. {
  7359. return p;
  7360. }
  7361. void CLocalWorkUnit::copyWorkUnit(IConstWorkUnit *cached, bool copyStats, bool all)
  7362. {
  7363. CLocalWorkUnit *from = QUERYINTERFACE(cached, CLocalWorkUnit);
  7364. if (!from)
  7365. {
  7366. CLockedWorkUnit *fl = QUERYINTERFACE(cached, CLockedWorkUnit);
  7367. if (!fl)
  7368. throw MakeStringException(WUERR_InternalUnknownImplementation, "Cached workunit not created using workunit dll");
  7369. from = fl->c;
  7370. }
  7371. // Need to copy the query, the results, and the graphs from the cached query.
  7372. // The cache is made before the query is executed so there is no need to clear them.
  7373. if (!cached->getCloneable())
  7374. throw MakeStringException(WUERR_CannotCloneWorkunit, "Source work unit not marked as clonable");
  7375. const IPropertyTree * fromP = from->p;
  7376. IPropertyTree *pt;
  7377. CriticalBlock block(crit);
  7378. clearCached(false);
  7379. query.clear();
  7380. updateProp(p, fromP, "@jobName");
  7381. copyTree(p, fromP, "Query");
  7382. pt = fromP->getBranch("Application");
  7383. if (pt)
  7384. synchronizePTree(ensurePTree(p, "Application"), pt, false, false);
  7385. pt = fromP->queryBranch("Debug");
  7386. if (pt)
  7387. {
  7388. IPropertyTree *curDebug = p->queryPropTree("Debug");
  7389. if (curDebug)
  7390. {
  7391. Owned<IPropertyTreeIterator> elems = pt->getElements("*");
  7392. ForEach(*elems)
  7393. {
  7394. IPropertyTree *elem = &elems->query();
  7395. if (!curDebug->hasProp(elem->queryName()))
  7396. curDebug->setPropTree(elem->queryName(),LINK(elem));
  7397. }
  7398. }
  7399. else
  7400. p->setPropTree("Debug", LINK(pt));
  7401. }
  7402. copyTree(p, fromP, "OnWarnings");
  7403. copyTree(p, fromP, "Plugins");
  7404. copyTree(p, fromP, "Libraries");
  7405. copyTree(p, fromP, "Results");
  7406. copyTree(p, fromP, "Graphs");
  7407. copyTree(p, fromP, "Workflow");
  7408. copyTree(p, fromP, "WebServicesInfo");
  7409. if (copyStats)
  7410. {
  7411. // Merge timing info from both branches
  7412. pt = fromP->getBranch("Statistics");
  7413. if (pt)
  7414. {
  7415. IPropertyTree *tgtStatistics = ensurePTree(p, "Statistics");
  7416. mergePTree(tgtStatistics, pt);
  7417. pt->Release();
  7418. }
  7419. }
  7420. updateProp(p, fromP, "@clusterName");
  7421. updateProp(p, fromP, "allowedclusters");
  7422. updateProp(p, fromP, "@submitID");
  7423. updateProp(p, fromP, "SNAPSHOT");
  7424. setProp(p, fromP, "@eventScheduledCount");
  7425. //MORE: This is very adhoc. All options that should be cloned should really be in a common branch
  7426. if (all)
  7427. {
  7428. setProp(p, fromP, "PriorityFlag");
  7429. setProp(p, fromP, "@priorityClass");
  7430. setProp(p, fromP, "@protected");
  7431. setProp(p, fromP, "@clusterName");
  7432. updateProp(p, fromP, "@scope");
  7433. }
  7434. //Variables may have been set up as parameters to the query - so need to preserve any values that were supplied.
  7435. pt = fromP->getBranch("Variables");
  7436. if (pt)
  7437. {
  7438. IPropertyTree *ptTgtVariables = ensurePTree(p, "Variables");
  7439. Owned<IPropertyTreeIterator> ptiVariable = pt->getElements("Variable");
  7440. for (ptiVariable->first(); ptiVariable->isValid(); ptiVariable->next())
  7441. {
  7442. IPropertyTree *ptSrcVariable = &ptiVariable->query();
  7443. const char *name = ptSrcVariable->queryProp("@name");
  7444. assertex(name);
  7445. StringBuffer xpath;
  7446. xpath.append("Variable[@name='").append(name).append("']");
  7447. IPropertyTree *ptTgtVariable = ptTgtVariables->queryPropTree(xpath.str());
  7448. IPropertyTree *merged = createPTreeFromIPT(ptSrcVariable); // clone entire source info...
  7449. merged->removeProp("Value"); // except value and status
  7450. merged->setProp("@status", "undefined");
  7451. if (!merged->getPropBool("@isScalar"))
  7452. merged->removeProp("totalRowCount");
  7453. merged->removeProp("rowCount");
  7454. // If there are any other fields that get set ONLY by eclagent, strip them out here...
  7455. if (ptTgtVariable)
  7456. {
  7457. // copy status and Value from what is already set in target
  7458. merged->setProp("@status", ptTgtVariable->queryProp("@status"));
  7459. MemoryBuffer value;
  7460. if (ptTgtVariable->getPropBin("Value", value))
  7461. merged->setPropBin("Value", value.length(), value.toByteArray());
  7462. ptTgtVariable->removeProp(xpath.str());
  7463. // If there are any other fields in a variable that get set by ws_ecl before submitting, copy them across here...
  7464. }
  7465. ptTgtVariables->addPropTree("Variable", merged);
  7466. }
  7467. pt->Release();
  7468. }
  7469. p->setProp("@codeVersion", fromP->queryProp("@codeVersion"));
  7470. p->setProp("@buildVersion", fromP->queryProp("@buildVersion"));
  7471. p->setProp("@eclVersion", fromP->queryProp("@eclVersion"));
  7472. p->setProp("@totalThorTime", fromP->queryProp("@totalThorTime"));
  7473. p->setProp("@hash", fromP->queryProp("@hash"));
  7474. p->setProp("@executeCost", fromP->queryProp("@executeCost"));
  7475. p->setProp("@fileAccessCost", fromP->queryProp("@fileAccessCost"));
  7476. p->setPropBool("@cloneable", true);
  7477. p->setPropBool("@isClone", true);
  7478. resetWorkflow(); // the source Workflow section may have had some parts already executed...
  7479. Owned<IPropertyTreeIterator> results = p->getElements("Results/Result");
  7480. ForEach(*results)
  7481. {
  7482. CLocalWUResult result(LINK(&results->query()));
  7483. result.setResultStatus(ResultStatusUndefined);
  7484. }
  7485. copyTree(p, fromP, "usedsources"); // field usage
  7486. }
  7487. bool CLocalWorkUnit::hasDebugValue(const char *propname) const
  7488. {
  7489. StringBuffer lower;
  7490. lower.append(propname).toLowerCase();
  7491. CriticalBlock block(crit);
  7492. StringBuffer prop("Debug/");
  7493. return p->hasProp(prop.append(lower));
  7494. }
  7495. IStringVal& CLocalWorkUnit::getDebugValue(const char *propname, IStringVal &str) const
  7496. {
  7497. StringBuffer lower;
  7498. lower.append(propname).toLowerCase();
  7499. CriticalBlock block(crit);
  7500. StringBuffer prop("Debug/");
  7501. str.set(p->queryProp(prop.append(lower).str()));
  7502. return str;
  7503. }
  7504. IStringIterator& CLocalWorkUnit::getDebugValues() const
  7505. {
  7506. return getDebugValues(NULL);
  7507. }
  7508. IStringIterator& CLocalWorkUnit::getDebugValues(const char *prop) const
  7509. {
  7510. CriticalBlock block(crit);
  7511. StringBuffer path("Debug/");
  7512. if (prop)
  7513. {
  7514. StringBuffer lower;
  7515. lower.append(prop).toLowerCase();
  7516. path.append(lower);
  7517. }
  7518. else
  7519. path.append("*");
  7520. return *new CStringPTreeTagIterator(p->getElements(path.str()));
  7521. }
  7522. int CLocalWorkUnit::getDebugValueInt(const char *propname, int defVal) const
  7523. {
  7524. StringBuffer lower;
  7525. lower.append(propname).toLowerCase();
  7526. CriticalBlock block(crit);
  7527. StringBuffer prop("Debug/");
  7528. prop.append(lower);
  7529. return p->getPropInt(prop.str(), defVal);
  7530. }
  7531. __int64 CLocalWorkUnit::getDebugValueInt64(const char *propname, __int64 defVal) const
  7532. {
  7533. StringBuffer lower;
  7534. lower.append(propname).toLowerCase();
  7535. CriticalBlock block(crit);
  7536. StringBuffer prop("Debug/");
  7537. prop.append(lower);
  7538. return p->getPropInt64(prop.str(), defVal);
  7539. }
  7540. double CLocalWorkUnit::getDebugValueReal(const char *propname, double defVal) const
  7541. {
  7542. StringBuffer lower;
  7543. lower.append(propname).toLowerCase();
  7544. CriticalBlock block(crit);
  7545. StringBuffer prop("Debug/");
  7546. prop.append(lower);
  7547. return p->getPropReal(prop.str(), defVal);
  7548. }
  7549. bool CLocalWorkUnit::getDebugValueBool(const char * propname, bool defVal) const
  7550. {
  7551. StringBuffer lower;
  7552. lower.append(propname).toLowerCase();
  7553. CriticalBlock block(crit);
  7554. StringBuffer prop("Debug/");
  7555. prop.append(lower);
  7556. return p->getPropBool(prop.str(), defVal);
  7557. }
  7558. IStringIterator *CLocalWorkUnit::getLogs(const char *type, const char *instance) const
  7559. {
  7560. VStringBuffer xpath("Process/%s/", type);
  7561. if (instance)
  7562. xpath.append(instance);
  7563. else
  7564. xpath.append("*");
  7565. CriticalBlock block(crit);
  7566. if (p->getPropInt("@wuidVersion") < 1) // legacy wuid
  7567. {
  7568. // NB: instance unused
  7569. if (streq("EclAgent", type))
  7570. return new CStringPTreeIterator(p->getElements("Debug/eclagentlog"));
  7571. else if (streq("Thor", type))
  7572. return new CStringPTreeIterator(p->getElements("Debug/thorlog*"));
  7573. VStringBuffer xpath("Debug/%s", type);
  7574. return new CStringPTreeIterator(p->getElements(xpath.str()));
  7575. }
  7576. else
  7577. return new CStringPTreeAttrIterator(p->getElements(xpath.str()), "@log");
  7578. }
  7579. IPropertyTreeIterator* CLocalWorkUnit::getProcesses(const char *type, const char *instance) const
  7580. {
  7581. VStringBuffer xpath("Process/%s/", type);
  7582. if (instance)
  7583. xpath.append(instance);
  7584. else
  7585. xpath.append("*");
  7586. CriticalBlock block(crit);
  7587. return p->getElements(xpath.str());
  7588. }
  7589. IStringIterator *CLocalWorkUnit::getProcesses(const char *type) const
  7590. {
  7591. VStringBuffer xpath("Process/%s/*", type);
  7592. CriticalBlock block(crit);
  7593. return new CStringPTreeTagIterator(p->getElements(xpath.str()));
  7594. }
  7595. void CLocalWorkUnit::addProcess(const char *type, const char *instance, unsigned pid,
  7596. unsigned max, const char *pattern, bool singleLog, const char *log)
  7597. {
  7598. VStringBuffer processType("Process/%s", type);
  7599. VStringBuffer xpath("%s/%s", processType.str(), instance);
  7600. if (log)
  7601. xpath.appendf("[@log=\"%s\"]", log);
  7602. CriticalBlock block(crit);
  7603. if (!p->hasProp(xpath))
  7604. {
  7605. IPropertyTree *node = ensurePTree(p, processType.str());
  7606. node = node->addPropTree(instance);
  7607. node->setProp("@log", log);
  7608. node->setPropInt("@pid", pid);
  7609. if (max > 0)
  7610. node->setPropInt("@max", max);
  7611. if (!isEmptyString(pattern))
  7612. node->setProp("@pattern", pattern);
  7613. if (singleLog)
  7614. node->setPropBool("@singleLog", true);
  7615. }
  7616. }
  7617. void CLocalWorkUnit::setDebugValue(const char *propname, const char *value, bool overwrite)
  7618. {
  7619. StringBuffer lower;
  7620. lower.append(propname).toLowerCase();
  7621. CriticalBlock block(crit);
  7622. StringBuffer prop("Debug/");
  7623. prop.append(lower);
  7624. if (overwrite || !p->hasProp(prop.str()))
  7625. {
  7626. // MORE - not sure this line should be needed....
  7627. p->setProp("Debug", "");
  7628. p->setProp(prop.str(), value);
  7629. }
  7630. }
  7631. void CLocalWorkUnit::setDebugValueInt(const char *propname, int value, bool overwrite)
  7632. {
  7633. StringBuffer lower;
  7634. lower.append(propname).toLowerCase();
  7635. CriticalBlock block(crit);
  7636. StringBuffer prop("Debug/");
  7637. prop.append(lower);
  7638. if (overwrite || !p->hasProp(prop.str()))
  7639. {
  7640. // MORE - not sure this line should be needed....
  7641. p->setProp("Debug", "");
  7642. p->setPropInt(prop.str(), value);
  7643. }
  7644. }
  7645. void CLocalWorkUnit::setTracingValue(const char *propname, const char *value)
  7646. {
  7647. CriticalBlock block(crit);
  7648. // MORE - not sure this line should be needed....
  7649. p->setProp("Tracing", "");
  7650. StringBuffer prop("Tracing/");
  7651. p->setProp(prop.append(propname).str(), value);
  7652. }
  7653. void CLocalWorkUnit::setTracingValueInt(const char *propname, int value)
  7654. {
  7655. CriticalBlock block(crit);
  7656. StringBuffer prop("Tracing/");
  7657. p->setPropInt(prop.append(propname).str(), value);
  7658. }
  7659. void CLocalWorkUnit::setTracingValueInt64(const char *propname, __int64 value)
  7660. {
  7661. CriticalBlock block(crit);
  7662. VStringBuffer prop("Tracing/%s", propname);
  7663. p->setPropInt64(prop.str(), value);
  7664. }
  7665. IConstWUQuery* CLocalWorkUnit::getQuery() const
  7666. {
  7667. // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
  7668. CriticalBlock block(crit);
  7669. if (!query)
  7670. {
  7671. IPropertyTree *s = queryMergedTree()->getPropTree("Query");
  7672. if (s)
  7673. query.setown(new CLocalWUQuery(s)); // NB takes ownership of 's'
  7674. }
  7675. return query.getLink();
  7676. }
  7677. IWUQuery* CLocalWorkUnit::updateQuery()
  7678. {
  7679. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  7680. CriticalBlock block(crit);
  7681. if (!query)
  7682. {
  7683. IPropertyTree *s = queryMergedTree()->queryPropTree("Query");
  7684. if (!s)
  7685. s = p->addPropTree("Query", createPTreeFromXMLString("<Query fetchEntire='1'/>")); // Is this really desirable (the fetchEntire) ?
  7686. s->Link();
  7687. query.setown(new CLocalWUQuery(s));
  7688. }
  7689. return query.getLink();
  7690. }
  7691. void CLocalWorkUnit::loadPlugins() const
  7692. {
  7693. CriticalBlock block(crit);
  7694. if (!pluginsCached)
  7695. {
  7696. assertex(plugins.length() == 0);
  7697. Owned<IPropertyTreeIterator> r = p->getElements("Plugins/Plugin");
  7698. for (r->first(); r->isValid(); r->next())
  7699. {
  7700. IPropertyTree *rp = &r->query();
  7701. rp->Link();
  7702. plugins.append(*new CLocalWUPlugin(rp));
  7703. }
  7704. pluginsCached = true;
  7705. }
  7706. }
  7707. IConstWUPluginIterator& CLocalWorkUnit::getPlugins() const
  7708. {
  7709. CriticalBlock block(crit);
  7710. loadPlugins();
  7711. return *new CArrayIteratorOf<IConstWUPlugin,IConstWUPluginIterator> (plugins, 0, (IConstWorkUnit *) this);
  7712. }
  7713. void CLocalWorkUnit::loadLibraries() const
  7714. {
  7715. CriticalBlock block(crit);
  7716. if (!librariesCached)
  7717. {
  7718. assertex(libraries.length() == 0);
  7719. Owned<IPropertyTreeIterator> r = p->getElements("Libraries/Library");
  7720. ForEach(*r)
  7721. {
  7722. IPropertyTree *rp = &r->query();
  7723. rp->Link();
  7724. libraries.append(*new CLocalWULibrary(rp));
  7725. }
  7726. librariesCached = true;
  7727. }
  7728. }
  7729. IConstWULibraryIterator& CLocalWorkUnit::getLibraries() const
  7730. {
  7731. CriticalBlock block(crit);
  7732. loadLibraries();
  7733. return *new CArrayIteratorOf<IConstWULibrary,IConstWULibraryIterator> (libraries, 0, (IConstWorkUnit *) this);
  7734. }
  7735. IConstWULibrary * CLocalWorkUnit::getLibraryByName(const char * search) const
  7736. {
  7737. CriticalBlock block(crit);
  7738. loadLibraries();
  7739. ForEachItemIn(idx, libraries)
  7740. {
  7741. SCMStringBuffer name;
  7742. IConstWULibrary &cur = libraries.item(idx);
  7743. cur.getName(name);
  7744. if (stricmp(name.str(), search)==0)
  7745. return &OLINK(cur);
  7746. }
  7747. return NULL;
  7748. }
  7749. StringBuffer &formatGraphTimerLabel(StringBuffer &str, const char *graphName, unsigned subGraphNum, unsigned __int64 subId)
  7750. {
  7751. str.append("Graph ").append(graphName);
  7752. if (subGraphNum) str.append(" - ").append(subGraphNum).append(" (").append(subId).append(")");
  7753. else if (subId) str.append(" - id(").append(subId).append(")");
  7754. return str;
  7755. }
  7756. StringBuffer &formatGraphTimerScope(StringBuffer &str, unsigned wfid, const char *graphName, unsigned subGraphNum, unsigned __int64 subId)
  7757. {
  7758. if (wfid)
  7759. str.append(WorkflowScopePrefix).append(wfid).append(":");
  7760. str.append(graphName);
  7761. if (subId) str.append(":sg").append(subId);
  7762. return str;
  7763. }
  7764. bool parseGraphTimerLabel(const char *label, StringAttr &graphName, unsigned & graphNum, unsigned &subGraphNum, unsigned &subId)
  7765. {
  7766. // expects format: "Graph <graphname>[ - <subgraphnum> (<subgraphid>)]"
  7767. unsigned len = (size32_t)strlen(label);
  7768. if (len < 6 || (0 != memcmp(label, "Graph ", 6)))
  7769. return false;
  7770. graphNum = 0;
  7771. subGraphNum = 0;
  7772. subId = 0;
  7773. const char *finger = label+6;
  7774. const char *finger2 = strchr(finger, '-');
  7775. if (NULL == finger2) // just graphName
  7776. graphName.set(finger);
  7777. else
  7778. {
  7779. graphName.set(finger, (size32_t)((finger2-1)-finger));
  7780. finger = finger2+2; // skip '-' and space
  7781. finger2 = strchr(finger, ' ');
  7782. if (finger2)
  7783. {
  7784. subGraphNum = atoi_l(finger, (size32_t)(finger2-finger));
  7785. finger = finger2+2; // skip space and '('
  7786. finger2 = strchr(finger, ')');
  7787. if (finger2)
  7788. subId = atoi_l(finger, (size32_t)(finger2-finger));
  7789. }
  7790. else if (((len-(finger-label))>3) && 0 == memcmp(finger, "id(", 3)) // subgraph id only, new format.
  7791. {
  7792. finger += 3;
  7793. finger2 = strchr(finger, ')');
  7794. if (finger2)
  7795. subId = atoi_l(finger, (size32_t)(finger2-finger));
  7796. }
  7797. }
  7798. if (graphName && !memicmp(graphName, "graph", 5))
  7799. graphNum = atoi(graphName + 5);
  7800. return true;
  7801. }
  7802. bool parseGraphScope(const char *scope, StringAttr &graphName, unsigned & graphNum, unsigned &subGraphId)
  7803. {
  7804. if (!MATCHES_CONST_PREFIX(scope, GraphScopePrefix))
  7805. return false;
  7806. graphNum = atoi(scope + strlen(GraphScopePrefix));
  7807. subGraphId = 0;
  7808. const char * colon = strchr(scope, ':');
  7809. if (!colon)
  7810. {
  7811. graphName.set(scope);
  7812. return true;
  7813. }
  7814. const char * subgraph = colon+1;
  7815. graphName.set(scope, (size32_t)(colon - scope));
  7816. if (MATCHES_CONST_PREFIX(subgraph, SubGraphScopePrefix))
  7817. subGraphId = atoi(subgraph+strlen(SubGraphScopePrefix));
  7818. return true;
  7819. }
  7820. void CLocalWorkUnit::setStatistic(StatisticCreatorType creatorType, const char * creator, StatisticScopeType scopeType, const char * scope, StatisticKind kind, const char * optDescription, unsigned __int64 value, unsigned __int64 count, unsigned __int64 maxValue, StatsMergeAction mergeAction)
  7821. {
  7822. if (!scope) scope = GLOBAL_SCOPE;
  7823. const char * kindName = queryStatisticName(kind);
  7824. StatisticMeasure measure = queryMeasure(kind);
  7825. //creator. scope and name must all be present, and must not contain semi colons.
  7826. assertex(creator && scope);
  7827. CriticalBlock block(crit);
  7828. IPropertyTree * stats = p->queryPropTree("Statistics");
  7829. if (!stats)
  7830. stats = p->addPropTree("Statistics");
  7831. IPropertyTree * statTree = NULL;
  7832. if (mergeAction != StatsMergeAppend)
  7833. {
  7834. StringBuffer xpath;
  7835. xpath.append("Statistic[@creator='").append(creator).append("'][@scope='").append(scope).append("'][@kind='").append(kindName).append("']");
  7836. statTree = stats->queryPropTree(xpath.str());
  7837. }
  7838. if (!statTree)
  7839. {
  7840. /* NB: Sasha archive uses this structure directly
  7841. * if it changes, the code in saarch.cpp needs updating
  7842. */
  7843. statTree = stats->addPropTree("Statistic");
  7844. statTree->setProp("@creator", creator);
  7845. statTree->setProp("@scope", scope);
  7846. statTree->setProp("@kind", kindName);
  7847. //These items are primarily here to facilitate filtering.
  7848. statTree->setProp("@unit", queryMeasureName(measure));
  7849. statTree->setProp("@c", queryCreatorTypeName(creatorType));
  7850. statTree->setProp("@s", queryScopeTypeName(scopeType));
  7851. statTree->setPropInt64("@ts", getTimeStampNowValue());
  7852. if (optDescription)
  7853. statTree->setProp("@desc", optDescription);
  7854. if (statistics.cached)
  7855. statistics.append(statTree); // links statTree
  7856. mergeAction = StatsMergeAppend;
  7857. }
  7858. if (mergeAction != StatsMergeAppend) // RKC->GH Is this right??
  7859. {
  7860. unsigned __int64 oldValue = statTree->getPropInt64("@value", 0);
  7861. unsigned __int64 oldCount = statTree->getPropInt64("@count", 0);
  7862. unsigned __int64 oldMax = statTree->getPropInt64("@max", 0);
  7863. if (oldMax < oldValue)
  7864. oldMax = oldValue;
  7865. statTree->setPropInt64("@value", mergeStatisticValue(oldValue, value, mergeAction));
  7866. statTree->setPropInt64("@count", count + oldCount);
  7867. if (maxValue > oldMax)
  7868. statTree->setPropInt64("@max", maxValue);
  7869. }
  7870. else
  7871. {
  7872. statTree->setPropInt64("@value", value);
  7873. statTree->setPropInt64("@count", count);
  7874. if (maxValue)
  7875. statTree->setPropInt64("@max", maxValue);
  7876. else
  7877. statTree->removeProp("@max");
  7878. }
  7879. //Whenever a graph time is updated recalculate the total time spent in thor, and save it
  7880. if ((scopeType == SSTgraph) && (kind == StTimeElapsed))
  7881. {
  7882. _loadStatistics();
  7883. stat_type totalTime = 0;
  7884. ForEachItemIn(i, statistics)
  7885. {
  7886. IConstWUStatistic & cur = statistics.item(i);
  7887. if ((cur.getScopeType() == SSTgraph) && (cur.getKind() == StTimeElapsed))
  7888. totalTime += cur.getValue();
  7889. }
  7890. StringBuffer t;
  7891. formatTimeCollatable(t, totalTime, false);
  7892. p->setProp("@totalThorTime", t);
  7893. }
  7894. if (scopeType == SSTglobal)
  7895. {
  7896. if (kind == StCostExecute)
  7897. p->setPropInt64("@costExecute", value);
  7898. else if (kind == StCostFileAccess)
  7899. p->setPropInt64("@costFileAccess", value);
  7900. }
  7901. }
  7902. void CLocalWorkUnit::_loadStatistics() const
  7903. {
  7904. statistics.load(p,"Statistics/*");
  7905. }
  7906. bool CLocalWorkUnit::getStatistic(stat_type & value, const char * scope, StatisticKind kind) const
  7907. {
  7908. //MORE: Optimize this....
  7909. WuScopeFilter filter;
  7910. filter.addScope(scope).setIncludeNesting(0).addRequiredStat(kind).addOutputStatistic(kind).finishedFilter();
  7911. Owned<IConstWUScopeIterator> stats = &getScopeIterator(filter);
  7912. if (stats->first())
  7913. return stats->getStat(kind, value);
  7914. return false;
  7915. }
  7916. IConstWUScopeIterator & CLocalWorkUnit::getScopeIterator(const WuScopeFilter & filter) const
  7917. {
  7918. assertex(filter.isOptimized());
  7919. WuScopeSourceFlags sources = filter.querySources();
  7920. Owned<CompoundStatisticsScopeIterator> compoundIter = new CompoundStatisticsScopeIterator(filter);
  7921. if (sources & SSFsearchGlobalStats)
  7922. {
  7923. CriticalBlock block(crit);
  7924. statistics.loadBranch(p,"Statistics");
  7925. Owned<IConstWUScopeIterator> localStats(new WorkUnitStatisticsScopeIterator(statistics, filter.queryIterFilter()));
  7926. compoundIter->addIter(localStats);
  7927. }
  7928. if (sources & SSFsearchGraphStats)
  7929. {
  7930. const char * wuid = p->queryName();
  7931. Owned<IConstWUScopeIterator> scopeIter(new CConstGraphProgressScopeIterator(wuid, filter.queryIterFilter(), filter.queryMinVersion()));
  7932. compoundIter->addIter(scopeIter);
  7933. }
  7934. if (sources & SSFsearchGraph)
  7935. {
  7936. Owned<IConstWUScopeIterator> graphIter(new GraphScopeIterator(this, filter.queryIterFilter()));
  7937. compoundIter->addIter(graphIter);
  7938. }
  7939. if (sources & SSFsearchWorkflow)
  7940. {
  7941. Owned<IConstWorkflowItemIterator> iter = getWorkflowItems();
  7942. if (iter)
  7943. {
  7944. Owned<IConstWUScopeIterator> workflowIter(new WorkflowStatisticsScopeIterator(iter));
  7945. compoundIter->addIter(workflowIter);
  7946. }
  7947. }
  7948. if (sources & SSFsearchExceptions)
  7949. {
  7950. Owned<IConstWUScopeIterator> notesIter(new NotesIterator(this,filter.queryIterFilter()));
  7951. compoundIter->addIter(notesIter);
  7952. }
  7953. return *compoundIter.getClear();
  7954. }
  7955. IWUPlugin* CLocalWorkUnit::updatePluginByName(const char *qname)
  7956. {
  7957. CriticalBlock block(crit);
  7958. IConstWUPlugin *existing = getPluginByName(qname);
  7959. if (existing)
  7960. return (IWUPlugin *) existing;
  7961. if (!plugins.length())
  7962. p->addPropTree("Plugins");
  7963. IPropertyTree *pl = p->queryPropTree("Plugins");
  7964. IPropertyTree *s = pl->addPropTree("Plugin");
  7965. s->Link();
  7966. IWUPlugin* q = new CLocalWUPlugin(s);
  7967. q->Link();
  7968. plugins.append(*q);
  7969. q->setPluginName(qname);
  7970. return q;
  7971. }
  7972. IConstWUPlugin* CLocalWorkUnit::getPluginByName(const char *qname) const
  7973. {
  7974. CriticalBlock block(crit);
  7975. loadPlugins();
  7976. ForEachItemIn(idx, plugins)
  7977. {
  7978. SCMStringBuffer name;
  7979. IConstWUPlugin &cur = plugins.item(idx);
  7980. cur.getPluginName(name);
  7981. if (stricmp(name.str(), qname)==0)
  7982. {
  7983. cur.Link();
  7984. return &cur;
  7985. }
  7986. }
  7987. return NULL;
  7988. }
  7989. IWULibrary* CLocalWorkUnit::updateLibraryByName(const char *qname)
  7990. {
  7991. CriticalBlock block(crit);
  7992. IConstWULibrary *existing = getLibraryByName(qname);
  7993. if (existing)
  7994. return (IWULibrary *) existing;
  7995. if (!libraries.length())
  7996. p->addPropTree("Libraries");
  7997. IPropertyTree *pl = p->queryPropTree("Libraries");
  7998. IPropertyTree *s = pl->addPropTree("Library");
  7999. s->Link();
  8000. IWULibrary* q = new CLocalWULibrary(s);
  8001. q->Link();
  8002. libraries.append(*q);
  8003. q->setName(qname);
  8004. return q;
  8005. }
  8006. void CLocalWorkUnit::_loadExceptions() const
  8007. {
  8008. assertex(exceptions.length() == 0);
  8009. Owned<IPropertyTreeIterator> r = p->getElements("Exceptions/Exception");
  8010. for (r->first(); r->isValid(); r->next())
  8011. {
  8012. IPropertyTree *rp = &r->query();
  8013. rp->Link();
  8014. exceptions.append(*new CLocalWUException(rp));
  8015. }
  8016. }
  8017. void CLocalWorkUnit::loadExceptions() const
  8018. {
  8019. CriticalBlock block(crit);
  8020. if (!exceptionsCached)
  8021. {
  8022. _loadExceptions();
  8023. exceptionsCached = true;
  8024. }
  8025. }
  8026. IConstWUExceptionIterator& CLocalWorkUnit::getExceptions() const
  8027. {
  8028. CriticalBlock block(crit);
  8029. loadExceptions();
  8030. return *new CArrayIteratorOf<IConstWUException,IConstWUExceptionIterator> (exceptions, 0, (IConstWorkUnit *) this);
  8031. }
  8032. unsigned CLocalWorkUnit::getExceptionCount() const
  8033. {
  8034. CriticalBlock block(crit);
  8035. loadExceptions();
  8036. return exceptions.length();
  8037. }
  8038. void CLocalWorkUnit::clearExceptions(const char *source)
  8039. {
  8040. CriticalBlock block(crit);
  8041. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  8042. loadExceptions();
  8043. ForEachItemInRev(idx, exceptions)
  8044. {
  8045. IWUException &e = exceptions.item(idx);
  8046. SCMStringBuffer s;
  8047. e.getExceptionSource(s);
  8048. if (source)
  8049. {
  8050. if (!strieq(s.s, source))
  8051. continue;
  8052. }
  8053. else
  8054. {
  8055. if (strieq(s.s, "eclcc") || strieq(s.s, "eclccserver") || strieq(s.s, "eclserver") )
  8056. break;
  8057. }
  8058. VStringBuffer xpath("Exceptions/Exception[@sequence='%d']", e.getSequence());
  8059. p->removeProp(xpath);
  8060. exceptions.remove(idx);
  8061. }
  8062. if (exceptions.length() == 0)
  8063. p->removeProp("Exceptions");
  8064. }
  8065. IWUException* CLocalWorkUnit::createException()
  8066. {
  8067. CriticalBlock block(crit);
  8068. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  8069. loadExceptions();
  8070. if (!exceptions.length())
  8071. p->addPropTree("Exceptions");
  8072. IPropertyTree *r = p->queryPropTree("Exceptions");
  8073. IPropertyTree *s = r->addPropTree("Exception");
  8074. s->setPropInt("@sequence", exceptions.ordinality());
  8075. IWUException* q = new CLocalWUException(LINK(s));
  8076. exceptions.append(*LINK(q));
  8077. Owned<IJlibDateTime> now = createDateTimeNow();
  8078. SCMStringBuffer temp;
  8079. now->getString(temp);
  8080. q->setTimeStamp(temp.str());
  8081. return q;
  8082. }
  8083. IConstWUWebServicesInfo* CLocalWorkUnit::getWebServicesInfo() const
  8084. {
  8085. // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
  8086. CriticalBlock block(crit);
  8087. if (!webServicesInfoCached)
  8088. {
  8089. assertex(!webServicesInfo);
  8090. IPropertyTree *s = p->getPropTree("WebServicesInfo");
  8091. if (s)
  8092. webServicesInfo.setown(new CLocalWUWebServicesInfo(s)); // NB takes ownership of 's'
  8093. webServicesInfoCached = true;
  8094. }
  8095. return webServicesInfo.getLink();
  8096. }
  8097. IWUWebServicesInfo* CLocalWorkUnit::updateWebServicesInfo(bool create)
  8098. {
  8099. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  8100. CriticalBlock block(crit);
  8101. if (!webServicesInfoCached)
  8102. {
  8103. IPropertyTree *s = p->queryPropTree("WebServicesInfo");
  8104. if (!s)
  8105. {
  8106. if (create)
  8107. s = p->addPropTree("WebServicesInfo");
  8108. else
  8109. return NULL;
  8110. }
  8111. s->Link();
  8112. webServicesInfo.setown(new CLocalWUWebServicesInfo(s));
  8113. webServicesInfoCached = true;
  8114. }
  8115. return webServicesInfo.getLink();
  8116. }
  8117. static int compareResults(IInterface * const *ll, IInterface * const *rr)
  8118. {
  8119. CLocalWUResult *l = (CLocalWUResult *) *ll;
  8120. CLocalWUResult *r = (CLocalWUResult *) *rr;
  8121. return l->getResultSequence() - r->getResultSequence();
  8122. }
  8123. void CLocalWorkUnit::_loadResults() const
  8124. {
  8125. Owned<IPropertyTreeIterator> r = p->getElements("Results/Result");
  8126. for (r->first(); r->isValid(); r->next())
  8127. {
  8128. IPropertyTree *rp = &r->query();
  8129. rp->Link();
  8130. results.append(*new CLocalWUResult(rp));
  8131. }
  8132. }
  8133. void CLocalWorkUnit::loadResults() const
  8134. {
  8135. if (!resultsCached)
  8136. {
  8137. assertex(results.length() == 0);
  8138. _loadResults();
  8139. results.sort(compareResults);
  8140. resultsCached = true;
  8141. }
  8142. }
  8143. void CLocalWorkUnit::_loadVariables() const
  8144. {
  8145. Owned<IPropertyTreeIterator> r = p->getElements("Variables/Variable");
  8146. for (r->first(); r->isValid(); r->next())
  8147. {
  8148. IPropertyTree *rp = &r->query();
  8149. rp->Link();
  8150. variables.append(*new CLocalWUResult(rp));
  8151. }
  8152. }
  8153. void CLocalWorkUnit::loadVariables() const
  8154. {
  8155. if (!variablesCached)
  8156. {
  8157. assertex(variables.length() == 0);
  8158. _loadVariables();
  8159. variablesCached = true;
  8160. }
  8161. }
  8162. void CLocalWorkUnit::_loadTemporaries() const
  8163. {
  8164. Owned<IPropertyTreeIterator> r = p->getElements("Temporaries/Variable");
  8165. for (r->first(); r->isValid(); r->next())
  8166. {
  8167. IPropertyTree *rp = &r->query();
  8168. rp->Link();
  8169. temporaries.append(*new CLocalWUResult(rp));
  8170. }
  8171. }
  8172. void CLocalWorkUnit::loadTemporaries() const
  8173. {
  8174. if (!temporariesCached)
  8175. {
  8176. assertex(temporaries.length() == 0);
  8177. _loadTemporaries();
  8178. temporariesCached = true;
  8179. }
  8180. }
  8181. void CLocalWorkUnit::deleteTemporaries()
  8182. {
  8183. CriticalBlock block(crit);
  8184. if (temporariesCached)
  8185. {
  8186. temporaries.kill();
  8187. temporariesCached = false;
  8188. }
  8189. p->removeProp("Temporaries");
  8190. }
  8191. IWUResult* CLocalWorkUnit::createResult()
  8192. {
  8193. CriticalBlock block(crit);
  8194. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  8195. loadResults();
  8196. if (!results.length())
  8197. p->addPropTree("Results");
  8198. IPropertyTree *r = p->queryPropTree("Results");
  8199. IPropertyTree *s = r->addPropTree("Result");
  8200. s->Link();
  8201. IWUResult* q = new CLocalWUResult(s);
  8202. q->Link();
  8203. results.append(*q);
  8204. return q;
  8205. }
  8206. IWUResult* CLocalWorkUnit::updateResultByName(const char *qname)
  8207. {
  8208. CriticalBlock block(crit);
  8209. IConstWUResult *existing = getResultByName(qname);
  8210. if (existing)
  8211. return (IWUResult *) existing;
  8212. IWUResult* q = createResult();
  8213. q->setResultName(qname);
  8214. return q;
  8215. }
  8216. IWUResult* CLocalWorkUnit::updateResultBySequence(unsigned seq)
  8217. {
  8218. CriticalBlock block(crit);
  8219. IConstWUResult *existing = getResultBySequence(seq);
  8220. if (existing)
  8221. return (IWUResult *) existing;
  8222. IWUResult* q = createResult();
  8223. q->setResultSequence(seq);
  8224. return q;
  8225. }
  8226. IConstWUResultIterator& CLocalWorkUnit::getResults() const
  8227. {
  8228. CriticalBlock block(crit);
  8229. loadResults();
  8230. return *new CArrayIteratorOf<IConstWUResult,IConstWUResultIterator> (results, 0, (IConstWorkUnit *) this);
  8231. }
  8232. IConstWUResult* CLocalWorkUnit::getResultByName(const char *qname) const
  8233. {
  8234. CriticalBlock block(crit);
  8235. loadResults();
  8236. ForEachItemIn(idx, results)
  8237. {
  8238. SCMStringBuffer name;
  8239. IConstWUResult &cur = results.item(idx);
  8240. cur.getResultName(name);
  8241. if (stricmp(name.str(), qname)==0)
  8242. {
  8243. cur.Link();
  8244. return &cur;
  8245. }
  8246. }
  8247. return NULL;
  8248. }
  8249. IConstWUResult* CLocalWorkUnit::getQueryResultByName(const char *qname) const
  8250. {
  8251. CriticalBlock block(crit);
  8252. loadResults();
  8253. ForEachItemIn(idx, results)
  8254. {
  8255. IConstWUResult &cur = results.item(idx);
  8256. if (!isSpecialResultSequence(cur.getResultSequence()))
  8257. {
  8258. SCMStringBuffer name;
  8259. cur.getResultName(name);
  8260. if (stricmp(name.str(), qname)==0)
  8261. {
  8262. cur.Link();
  8263. return &cur;
  8264. }
  8265. }
  8266. }
  8267. return NULL;
  8268. }
  8269. IConstWUResult* CLocalWorkUnit::getResultBySequence(unsigned seq) const
  8270. {
  8271. CriticalBlock block(crit);
  8272. loadResults();
  8273. ForEachItemIn(idx, results)
  8274. {
  8275. IConstWUResult &cur = results.item(idx);
  8276. if (cur.getResultSequence() == seq)
  8277. {
  8278. cur.Link();
  8279. return &cur;
  8280. }
  8281. }
  8282. return NULL;
  8283. }
  8284. IConstWUResultIterator& CLocalWorkUnit::getVariables() const
  8285. {
  8286. CriticalBlock block(crit);
  8287. loadVariables();
  8288. return *new CArrayIteratorOf<IConstWUResult,IConstWUResultIterator> (variables, 0, (IConstWorkUnit *) this);
  8289. }
  8290. IConstWUResult* CLocalWorkUnit::getGlobalByName(const char *qname) const
  8291. {
  8292. CriticalBlock block(crit);
  8293. if (strcmp(p->queryName(), GLOBAL_WORKUNIT)==0)
  8294. return getVariableByName(qname);
  8295. Owned <IWorkUnit> global = globalFactory->getGlobalWorkUnit(secMgr, secUser);
  8296. return global->getVariableByName(qname);
  8297. }
  8298. IWUResult* CLocalWorkUnit::updateGlobalByName(const char *qname)
  8299. {
  8300. CriticalBlock block(crit);
  8301. if (strcmp(p->queryName(), GLOBAL_WORKUNIT)==0)
  8302. return updateVariableByName(qname);
  8303. Owned <IWorkUnit> global = globalFactory->getGlobalWorkUnit(secMgr, secUser);
  8304. return global->updateVariableByName(qname);
  8305. }
  8306. IConstWUResult* CLocalWorkUnit::getVariableByName(const char *qname) const
  8307. {
  8308. CriticalBlock block(crit);
  8309. loadVariables();
  8310. ForEachItemIn(idx, variables)
  8311. {
  8312. SCMStringBuffer name;
  8313. IConstWUResult &cur = variables.item(idx);
  8314. cur.getResultName(name);
  8315. if (stricmp(name.str(), qname)==0)
  8316. {
  8317. cur.Link();
  8318. return &cur;
  8319. }
  8320. }
  8321. return NULL;
  8322. }
  8323. IConstWUResult* CLocalWorkUnit::getTemporaryByName(const char *qname) const
  8324. {
  8325. CriticalBlock block(crit);
  8326. loadTemporaries();
  8327. ForEachItemIn(idx, temporaries)
  8328. {
  8329. SCMStringBuffer name;
  8330. IConstWUResult &cur = temporaries.item(idx);
  8331. cur.getResultName(name);
  8332. if (stricmp(name.str(), qname)==0)
  8333. {
  8334. cur.Link();
  8335. return &cur;
  8336. }
  8337. }
  8338. return NULL;
  8339. }
  8340. IConstWUResultIterator& CLocalWorkUnit::getTemporaries() const
  8341. {
  8342. CriticalBlock block(crit);
  8343. loadTemporaries();
  8344. return *new CArrayIteratorOf<IConstWUResult,IConstWUResultIterator> (temporaries, 0, (IConstWorkUnit *) this);
  8345. }
  8346. IWUResult* CLocalWorkUnit::updateTemporaryByName(const char *qname)
  8347. {
  8348. CriticalBlock block(crit);
  8349. IConstWUResult *existing = getTemporaryByName(qname);
  8350. if (existing)
  8351. return (IWUResult *) existing;
  8352. IPropertyTree *vars = (temporaries.length()) ? p->queryPropTree("Temporaries") : p->addPropTree("Temporaries");
  8353. IPropertyTree *s = vars->addPropTree("Variable");
  8354. s->Link();
  8355. IWUResult* q = new CLocalWUResult(s);
  8356. q->Link();
  8357. temporaries.append(*q);
  8358. q->setResultName(qname);
  8359. q->setResultSequence(ResultSequenceInternal);
  8360. return q;
  8361. }
  8362. IWUResult* CLocalWorkUnit::updateVariableByName(const char *qname)
  8363. {
  8364. CriticalBlock block(crit);
  8365. IConstWUResult *existing = getVariableByName(qname);
  8366. if (existing)
  8367. return (IWUResult *) existing;
  8368. if (!variables.length())
  8369. p->addPropTree("Variables");
  8370. IPropertyTree *vars = p->queryPropTree("Variables");
  8371. IPropertyTree *s = vars->addPropTree("Variable");
  8372. s->Link();
  8373. IWUResult* q = new CLocalWUResult(s);
  8374. q->Link();
  8375. variables.append(*q);
  8376. q->setResultName(qname);
  8377. q->setResultSequence(ResultSequenceStored);
  8378. return q;
  8379. }
  8380. void CLocalWorkUnit::deleteTempFiles(const char *graph, bool deleteOwned, bool deleteJobOwned)
  8381. {
  8382. CriticalBlock block(crit);
  8383. IPropertyTree *files = p->queryPropTree("Files");
  8384. if (!files) return;
  8385. Owned<IPropertyTreeIterator> iter = files->getElements("File");
  8386. ICopyArrayOf<IPropertyTree> toRemove;
  8387. ForEach (*iter)
  8388. {
  8389. IPropertyTree &file = iter->query();
  8390. WUFileKind fileKind = (WUFileKind) file.getPropInt("@kind", WUFileStandard);
  8391. if(file.getPropBool("@temporary")) fileKind = WUFileTemporary; // @temporary, legacy check
  8392. bool needDelete;
  8393. switch(fileKind)
  8394. {
  8395. case WUFileTemporary:
  8396. if(graph==NULL)
  8397. needDelete = true;
  8398. else
  8399. {
  8400. const char *graphOwner = file.queryProp("@graph");
  8401. needDelete = ((graphOwner==NULL) || (strcmp(graph, graphOwner)==0));
  8402. }
  8403. break;
  8404. case WUFileJobOwned:
  8405. needDelete = ((graph==NULL) && deleteJobOwned);
  8406. break;
  8407. case WUFileOwned:
  8408. needDelete = ((graph==NULL) && deleteOwned);
  8409. break;
  8410. default:
  8411. needDelete = false;
  8412. }
  8413. if(needDelete)
  8414. {
  8415. const char *name = file.queryProp("@name");
  8416. LOG(MCdebugProgress, unknownJob, "Removing workunit file %s from DFS", name);
  8417. queryDistributedFileDirectory().removeEntry(name, queryUserDescriptor());
  8418. toRemove.append(file);
  8419. }
  8420. }
  8421. ForEachItemIn(r, toRemove) files->removeTree(&toRemove.item(r));
  8422. }
  8423. static void _noteFileRead(IDistributedFile *file, IPropertyTree *filesRead)
  8424. {
  8425. IDistributedSuperFile *super = file->querySuperFile();
  8426. StringBuffer fname;
  8427. file->getLogicalName(fname);
  8428. if (fname.length())
  8429. {
  8430. StringBuffer path("File[@name=\"");
  8431. path.append(fname).append("\"]");
  8432. IPropertyTree *fileTree = filesRead->queryPropTree(path.str());
  8433. if (fileTree)
  8434. fileTree->setPropInt("@useCount", fileTree->getPropInt("@useCount")+1);
  8435. else
  8436. {
  8437. StringBuffer cluster;
  8438. file->getClusterName(0,cluster);
  8439. fileTree = createPTree();
  8440. fileTree->setProp("@name", fname.str());
  8441. fileTree->setProp("@cluster", cluster.str());
  8442. fileTree->setPropInt("@useCount", 1);
  8443. fileTree = filesRead->addPropTree("File", fileTree);
  8444. }
  8445. if (super)
  8446. {
  8447. fileTree->setPropBool("@super", true);
  8448. Owned<IDistributedFileIterator> iter = super->getSubFileIterator(false);
  8449. ForEach (*iter)
  8450. {
  8451. IDistributedFile &file = iter->query();
  8452. StringBuffer fname;
  8453. file.getLogicalName(fname);
  8454. fileTree->addPropTree("Subfile")->setProp("@name", fname.str());
  8455. _noteFileRead(&file, filesRead);
  8456. }
  8457. }
  8458. }
  8459. }
  8460. void CLocalWorkUnit::_loadFilesRead() const
  8461. {
  8462. // Nothing to do
  8463. }
  8464. void CLocalWorkUnit::noteFileRead(IDistributedFile *file)
  8465. {
  8466. if (file)
  8467. {
  8468. CriticalBlock block(crit);
  8469. IPropertyTree *files = p->queryPropTree("FilesRead");
  8470. if (!files)
  8471. files = p->addPropTree("FilesRead");
  8472. _noteFileRead(file, files);
  8473. }
  8474. }
  8475. void CLocalWorkUnit::noteFieldUsage(IPropertyTree * fieldUsage)
  8476. {
  8477. if (fieldUsage)
  8478. {
  8479. CriticalBlock block(crit);
  8480. p->addPropTree("usedsources", fieldUsage);
  8481. }
  8482. }
  8483. void CLocalWorkUnit::_loadFilesWritten() const
  8484. {
  8485. // Nothing to do
  8486. }
  8487. static void addFile(IPropertyTree *files, const char *fileName, const char *cluster, unsigned usageCount, WUFileKind fileKind, const char *graphOwner)
  8488. {
  8489. StringBuffer path("File[@name=\"");
  8490. path.append(fileName).append("\"]");
  8491. if (cluster)
  8492. path.append("[@cluster=\"").append(cluster).append("\"]");
  8493. IPropertyTree *file = files->queryPropTree(path.str());
  8494. if (file) files->removeTree(file);
  8495. file = createPTree();
  8496. file->setProp("@name", fileName);
  8497. if (cluster)
  8498. file->setProp("@cluster", cluster);
  8499. if (graphOwner)
  8500. file->setProp("@graph", graphOwner);
  8501. file->setPropInt("@kind", (unsigned)fileKind);
  8502. if (WUFileTemporary == fileKind)
  8503. file->setPropInt("@usageCount", usageCount);
  8504. files->addPropTree("File", file);
  8505. }
  8506. void CLocalWorkUnit::addFile(const char *fileName, StringArray *clusters, unsigned usageCount, WUFileKind fileKind, const char *graphOwner)
  8507. {
  8508. CriticalBlock block(crit);
  8509. IPropertyTree *files = p->queryPropTree("Files");
  8510. if (!files)
  8511. files = p->addPropTree("Files");
  8512. if (!clusters)
  8513. ::addFile(files, fileName, NULL, usageCount, fileKind, graphOwner);
  8514. else
  8515. {
  8516. ForEachItemIn(c, *clusters)
  8517. ::addFile(files, fileName, clusters->item(c), usageCount, fileKind, graphOwner);
  8518. }
  8519. }
  8520. void CLocalWorkUnit::releaseFile(const char *fileName)
  8521. {
  8522. StringBuffer path("File[@name=\"");
  8523. path.append(fileName).append("\"]");
  8524. CriticalBlock block(crit);
  8525. IPropertyTree *files = p->queryPropTree("Files");
  8526. if (!files) return;
  8527. Owned<IPropertyTreeIterator> fiter = files->getElements(path.str());
  8528. if (fiter->first())
  8529. {
  8530. while (true)
  8531. {
  8532. IPropertyTree *file = &fiter->query();
  8533. unsigned usageCount = file->getPropInt("@usageCount");
  8534. bool more = fiter->next();
  8535. if (usageCount > 1)
  8536. file->setPropInt("@usageCount", usageCount-1);
  8537. else
  8538. {
  8539. StringAttr name(file->queryProp("@name"));
  8540. files->removeTree(file);
  8541. if (!name.isEmpty()&&(1 == usageCount))
  8542. {
  8543. if (queryDistributedFileDirectory().removeEntry(fileName, queryUserDescriptor()))
  8544. LOG(MCdebugProgress, unknownJob, "Removed (released) file %s from DFS", name.get());
  8545. }
  8546. }
  8547. if (!more)
  8548. break;
  8549. }
  8550. }
  8551. }
  8552. void CLocalWorkUnit::clearGraphProgress() const
  8553. {
  8554. }
  8555. void CLocalWorkUnit::resetBeforeGeneration()
  8556. {
  8557. CriticalBlock block(crit);
  8558. //Remove all associated files
  8559. Owned<IWUQuery> q = updateQuery();
  8560. q->removeAssociatedFiles();
  8561. //Remove any pre-existing workflow information
  8562. workflowIterator.clear();
  8563. p->removeProp("Workflow");
  8564. }
  8565. unsigned CLocalWorkUnit::queryFileUsage(const char *fileName) const
  8566. {
  8567. StringBuffer path("Files/File[@name=\"");
  8568. path.append(fileName).append("\"]/@usageCount");
  8569. CriticalBlock block(crit);
  8570. return p->getPropInt(path.str());
  8571. }
  8572. IConstWUFileUsageIterator * CLocalWorkUnit::getFieldUsage() const
  8573. {
  8574. CriticalBlock block(crit);
  8575. IPropertyTree* fieldUsageTree = p->queryPropTree("usedsources");
  8576. if (!fieldUsageTree)
  8577. return NULL;
  8578. IPropertyTreeIterator* iter = fieldUsageTree->getElements("*");
  8579. return new CConstWUFileUsageIterator(iter);
  8580. }
  8581. bool isFilenameResolved(StringBuffer& filename)
  8582. {
  8583. size32_t length = filename.length();
  8584. // With current implementation, if filename is surrounded by single quotes, it means that the filename was resolved at compile time.
  8585. if (filename.length() >= 2 && filename.charAt(0) == '\'' && filename.charAt(length-1) == '\'')
  8586. return true;
  8587. else
  8588. return false;
  8589. }
  8590. bool CLocalWorkUnit::getFieldUsageArray(StringArray & filenames, StringArray & columnnames, const char * clusterName) const
  8591. {
  8592. bool scopeLoaded = false;
  8593. StringBuffer defaultScope;
  8594. Owned<IConstWUFileUsageIterator> files = getFieldUsage();
  8595. if (!files)
  8596. return false; // this query was not compiled with recordFieldUsage option.
  8597. ForEach(*files)
  8598. {
  8599. Owned<IConstWUFileUsage> file = files->get();
  8600. StringBuffer filename(file->queryName());
  8601. size32_t length = filename.length();
  8602. if (length == 0)
  8603. throw MakeStringException(WUERR_InvalidFieldUsage, "Invalid FieldUsage found in WU. Cannot enforce view security.");
  8604. StringBuffer normalizedFilename;
  8605. // Two cases to handle:
  8606. // 1. Filename was known at compile time, and is surrounded in single quotes (i.e. 'filename').
  8607. // 2. Filename could not be resolved at compile time (i.e. filename is an input to a query),
  8608. // and is a raw expression WITHOUT surrounding single quotes (i.e. STORED('input_filename')).
  8609. if (isFilenameResolved(filename))
  8610. {
  8611. // filename cannot be empty (i.e. empty single quotes '')
  8612. if (length == 2)
  8613. throw MakeStringException(WUERR_InvalidFieldUsage, "Invalid FieldUsage found in WU. Cannot enforce view security.");
  8614. // Remove surrounding single quotes
  8615. StringAttr cleanFilename(filename.str()+1, length-2);
  8616. // When a filename doesn't start with a tilde (~), it means scope is omitted and is relying on a default scope.
  8617. // We need to load a default scope from config and prefix the filename with it.
  8618. if (cleanFilename.str()[0] != '~')
  8619. {
  8620. // loading a default scope from config is expensive, and should be only done once and be reused later.
  8621. if (!scopeLoaded)
  8622. {
  8623. //MORE: This should actually depend on the cluster that was active when the file was read!
  8624. if (!resolveFilePrefix(defaultScope, clusterName))
  8625. throw MakeStringException(WUERR_InvalidCluster, "Unknown cluster %s", clusterName);
  8626. scopeLoaded = true;
  8627. }
  8628. normalizedFilename.append(defaultScope.str());
  8629. normalizedFilename.append(cleanFilename.str());
  8630. }
  8631. else
  8632. {
  8633. normalizedFilename.append(cleanFilename);
  8634. }
  8635. }
  8636. else
  8637. {
  8638. // When filename is an unresolved expression, simply treat the expression as a "non-existent" filename.
  8639. // It will have an effect of this query accessing a non-existent filename, and will be denied access unconditionally.
  8640. normalizedFilename.append(filename.str());
  8641. }
  8642. Owned<IConstWUFieldUsageIterator> fields = file->getFields();
  8643. ForEach(*fields)
  8644. {
  8645. Owned<IConstWUFieldUsage> field = fields->get();
  8646. filenames.append(normalizedFilename.str());
  8647. columnnames.append(field->queryName());
  8648. }
  8649. }
  8650. return true;
  8651. }
  8652. IPropertyTree *CLocalWorkUnit::getDiskUsageStats()
  8653. {
  8654. return p->getPropTree("DiskUsageStats");
  8655. }
  8656. void CLocalWorkUnit::addDiskUsageStats(__int64 _avgNodeUsage, unsigned _minNode, __int64 _minNodeUsage, unsigned _maxNode, __int64 _maxNodeUsage, __int64 _graphId)
  8657. {
  8658. IPropertyTree *stats = p->queryPropTree("DiskUsageStats");
  8659. offset_t maxNodeUsage;
  8660. if (stats)
  8661. maxNodeUsage = stats->getPropInt64("@maxNodeUsage");
  8662. else
  8663. {
  8664. stats = p->addPropTree("DiskUsageStats");
  8665. maxNodeUsage = 0;
  8666. }
  8667. if ((offset_t)_maxNodeUsage > maxNodeUsage)
  8668. {
  8669. // record all details at time of max node usage.
  8670. stats->setPropInt("@minNode", _minNode);
  8671. stats->setPropInt("@maxNode", _maxNode);
  8672. stats->setPropInt64("@minNodeUsage", _minNodeUsage);
  8673. stats->setPropInt64("@maxNodeUsage", _maxNodeUsage);
  8674. stats->setPropInt64("@graphId", _graphId);
  8675. if (_avgNodeUsage)
  8676. {
  8677. unsigned _skewHi = (unsigned)((100 * (_maxNodeUsage-_avgNodeUsage))/_avgNodeUsage);
  8678. unsigned _skewLo = (unsigned)((100 * (_avgNodeUsage-_minNodeUsage))/_avgNodeUsage);
  8679. stats->setPropInt("@skewHi", _skewHi);
  8680. stats->setPropInt("@skewLo", _skewLo);
  8681. }
  8682. }
  8683. }
  8684. IPropertyTreeIterator & CLocalWorkUnit::getFileIterator() const
  8685. {
  8686. CriticalBlock block(crit);
  8687. _loadFilesWritten();
  8688. return * p->getElements("Files/File");
  8689. }
  8690. IPropertyTreeIterator & CLocalWorkUnit::getFilesReadIterator() const
  8691. {
  8692. CriticalBlock block(crit);
  8693. _loadFilesRead();
  8694. return * p->getElements("FilesRead/File");
  8695. }
  8696. //=================================================================================================
  8697. bool CLocalWorkUnit::switchThorQueue(const char *cluster, IQueueSwitcher *qs)
  8698. {
  8699. CriticalBlock block(crit);
  8700. if (qs->isAuto()&&!getAllowAutoQueueSwitch())
  8701. return false;
  8702. const char * currentcluster = queryClusterName();
  8703. const char *wuid = p->queryName();
  8704. StringBuffer curqname;
  8705. getClusterThorQueueName(curqname, currentcluster);
  8706. void *qi = qs->getQ(curqname.str(),wuid);
  8707. if (!qi)
  8708. return false;
  8709. setClusterName(cluster);
  8710. StringBuffer newqname;
  8711. getClusterThorQueueName(newqname, cluster);
  8712. qs->putQ(newqname.str(),wuid,qi);
  8713. return true;
  8714. }
  8715. //=================================================================================================
  8716. IPropertyTree *CLocalWorkUnit::getUnpackedTree(bool includeProgress) const
  8717. {
  8718. Owned<IPropertyTree> ret = createPTreeFromIPT(queryMergedTree());
  8719. Owned<IConstWUGraphIterator> graphIter = &getGraphs(GraphTypeAny);
  8720. ForEach(*graphIter)
  8721. {
  8722. IConstWUGraph &graph = graphIter->query();
  8723. Owned<IPropertyTree> graphTree = graph.getXGMMLTree(includeProgress, false);
  8724. SCMStringBuffer gName;
  8725. graph.getName(gName);
  8726. StringBuffer xpath("Graphs/Graph[@name=\"");
  8727. xpath.append(gName.s).append("\"]/xgmml");
  8728. IPropertyTree *xgmml = ret->queryPropTree(xpath.str());
  8729. if (xgmml) // don't know of any reason it shouldn't exist
  8730. {
  8731. xgmml->removeProp("graphBin");
  8732. xgmml->setPropTree("graph", graphTree.getClear());
  8733. }
  8734. }
  8735. return ret.getClear();
  8736. }
  8737. IPropertyTree *CLocalWorkUnit::queryMergedTree() const
  8738. {
  8739. if (indirectTree)
  8740. return indirectTree;
  8741. if (!p->hasProp("@clonedFromWorkunit"))
  8742. return p;
  8743. else
  8744. {
  8745. indirectTree.setown(createPTreeFromIPT(p));
  8746. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  8747. Owned<IConstWorkUnit> donor = factory->openWorkUnit(p->queryProp("@clonedFromWorkunit"));
  8748. if (donor)
  8749. {
  8750. copyTree(indirectTree, queryExtendedWU(donor)->queryPTree(), "Graphs");
  8751. copyTree(indirectTree, queryExtendedWU(donor)->queryPTree(), "Query");
  8752. }
  8753. return indirectTree;
  8754. }
  8755. }
  8756. void CLocalWorkUnit::_loadGraphs(bool heavy) const
  8757. {
  8758. Owned<IPropertyTreeIterator> iter = queryMergedTree()->getElements("Graphs/Graph");
  8759. ForEach(*iter)
  8760. {
  8761. IPropertyTree &graph = iter->query();
  8762. graphs.append(*new CLocalWUGraph(*this, LINK(&graph)));
  8763. }
  8764. }
  8765. void CLocalWorkUnit::loadGraphs(bool heavy) const
  8766. {
  8767. if (graphsCached < (heavy ? 2 : 1))
  8768. {
  8769. graphs.kill();
  8770. _loadGraphs(heavy);
  8771. graphsCached = (heavy ? 2 : 1);
  8772. }
  8773. }
  8774. EnumMapping graphTypes[] = {
  8775. { GraphTypeAny, "unknown" },
  8776. { GraphTypeProgress, "progress" },
  8777. { GraphTypeEcl, "ECL" },
  8778. { GraphTypeActivities, "activities" },
  8779. { GraphTypeSubProgress, "subgraph" },
  8780. { GraphTypeSize, NULL },
  8781. };
  8782. WUGraphType getGraphTypeFromString(const char* type)
  8783. {
  8784. return (WUGraphType) getEnum(type, graphTypes);
  8785. }
  8786. CLocalWUGraph::CLocalWUGraph(const CLocalWorkUnit &_owner, IPropertyTree *props) : p(props), owner(_owner)
  8787. {
  8788. wuidVersion = owner.getWuidVersion();
  8789. }
  8790. IStringVal& CLocalWUGraph::getName(IStringVal &str) const
  8791. {
  8792. str.set(p->queryProp("@name"));
  8793. return str;
  8794. }
  8795. IStringVal& CLocalWUGraph::getLabel(IStringVal &str) const
  8796. {
  8797. if (wuidVersion >= 2)
  8798. {
  8799. str.set(p->queryProp("@label"));
  8800. return str;
  8801. }
  8802. else
  8803. {
  8804. Owned<IPropertyTree> xgmml = getXGMMLTree(false, false);
  8805. str.set(xgmml->queryProp("@label"));
  8806. return str;
  8807. }
  8808. }
  8809. WUGraphState CLocalWUGraph::getState() const
  8810. {
  8811. return owner.queryGraphState(p->queryProp("@name"));
  8812. }
  8813. IStringVal& CLocalWUGraph::getXGMML(IStringVal &str, bool mergeProgress, bool doFormatStats) const
  8814. {
  8815. Owned<IPropertyTree> xgmml = getXGMMLTree(mergeProgress, doFormatStats);
  8816. if (xgmml)
  8817. {
  8818. StringBuffer x;
  8819. toXML(xgmml, x);
  8820. str.set(x.str());
  8821. }
  8822. return str;
  8823. }
  8824. unsigned CLocalWorkUnit::getGraphCount() const
  8825. {
  8826. CriticalBlock block(crit);
  8827. if (queryMergedTree()->hasProp("Graphs"))
  8828. {
  8829. return queryMergedTree()->queryPropTree("Graphs")->numChildren();
  8830. }
  8831. return 0;
  8832. }
  8833. unsigned CLocalWorkUnit::getSourceFileCount() const
  8834. {
  8835. CriticalBlock block(crit);
  8836. _loadFilesRead();
  8837. if (p->hasProp("FilesRead"))
  8838. {
  8839. return p->queryPropTree("FilesRead")->numChildren();
  8840. }
  8841. return 0;
  8842. }
  8843. unsigned CLocalWorkUnit::getResultCount() const
  8844. {
  8845. CriticalBlock block(crit);
  8846. if (p->hasProp("Results"))
  8847. {
  8848. return p->queryPropTree("Results")->numChildren();
  8849. }
  8850. return 0;
  8851. }
  8852. unsigned CLocalWorkUnit::getVariableCount() const
  8853. {
  8854. CriticalBlock block(crit);
  8855. if (p->hasProp("Variables"))
  8856. {
  8857. return p->queryPropTree("Variables")->numChildren();
  8858. }
  8859. return 0;
  8860. }
  8861. unsigned CLocalWorkUnit::getApplicationValueCount() const
  8862. {
  8863. CriticalBlock block(crit);
  8864. if (p->hasProp("Application"))
  8865. {
  8866. return p->queryPropTree("Application")->numChildren();
  8867. }
  8868. return 0;
  8869. }
  8870. StringBuffer &appendPTreeOpenTag(StringBuffer &s, IPropertyTree *tree, const char *name, unsigned indent, bool hidePasswords)
  8871. {
  8872. appendXMLOpenTag(s, name, NULL, false);
  8873. Owned<IAttributeIterator> attrs = tree->getAttributes(true);
  8874. if (attrs->first())
  8875. {
  8876. unsigned attributeindent = indent + (size32_t) strlen(name);
  8877. unsigned count = attrs->count();
  8878. bool doindent = false;
  8879. ForEach(*attrs)
  8880. {
  8881. if (hidePasswords && streq(attrs->queryName(), "@token"))
  8882. continue;
  8883. #ifndef _DEBUG
  8884. if (hidePasswords && streq(attrs->queryName(), "@distributedAccessToken"))
  8885. continue;
  8886. #endif
  8887. if (doindent)
  8888. s.append('\n').appendN(attributeindent, ' ');
  8889. else if (count > 3)
  8890. doindent = true;
  8891. appendXMLAttr(s, attrs->queryName()+1, attrs->queryValue());
  8892. }
  8893. }
  8894. s.append('>');
  8895. return s;
  8896. }
  8897. IStringVal &CLocalWorkUnit::getXmlParams(IStringVal &str, bool hidePasswords) const
  8898. {
  8899. CriticalBlock block(crit);
  8900. IPropertyTree *paramTree = p->queryPropTree("Parameters");
  8901. if (!paramTree)
  8902. return str;
  8903. StringBuffer xml;
  8904. if (!hidePasswords)
  8905. toXML(paramTree, xml);
  8906. else
  8907. {
  8908. appendPTreeOpenTag(xml.append(' '), paramTree, "Parameters", 0, false).append('\n');
  8909. Owned<IPropertyTreeIterator> elems = paramTree->getElements("*");
  8910. ForEach(*elems)
  8911. {
  8912. const char *paramname = elems->query().queryName();
  8913. VStringBuffer xpath("Variables/Variable[@name='%s']/Format/@password", paramname);
  8914. if (p->getPropBool(xpath))
  8915. appendXMLTag(xml.append(" "), paramname, "***").append('\n');
  8916. else
  8917. toXML(&elems->query(), xml, 2);
  8918. }
  8919. appendXMLCloseTag(xml.append(' '), "Parameters").append('\n');
  8920. }
  8921. str.set(xml);
  8922. return str;
  8923. }
  8924. const IPropertyTree *CLocalWorkUnit::getXmlParams() const
  8925. {
  8926. CriticalBlock block(crit);
  8927. return p->getPropTree("Parameters");
  8928. }
  8929. void CLocalWorkUnit::setXmlParams(const char *params)
  8930. {
  8931. CriticalBlock block(crit);
  8932. p->setPropTree("Parameters", createPTreeFromXMLString(params));
  8933. }
  8934. void CLocalWorkUnit::setXmlParams(IPropertyTree *tree)
  8935. {
  8936. CriticalBlock block(crit);
  8937. p->setPropTree("Parameters", tree);
  8938. }
  8939. unsigned __int64 CLocalWorkUnit::getHash() const
  8940. {
  8941. CriticalBlock block(crit);
  8942. return p->getPropInt64("@hash");
  8943. }
  8944. void CLocalWorkUnit::setHash(unsigned __int64 hash)
  8945. {
  8946. CriticalBlock block(crit);
  8947. p->setPropInt64("@hash", hash);
  8948. }
  8949. // getGraphs / getGraphsMeta
  8950. // These are basically the same except for the amount of preloading they do, and the type of the iterator they return...
  8951. // If a type other than any is requested, a postfilter is needed.
  8952. template <class T, class U> class CFilteredGraphIteratorOf : public CInterfaceOf<T>
  8953. {
  8954. WUGraphType type;
  8955. Owned<T> base;
  8956. bool match()
  8957. {
  8958. return base->query().getType()==type;
  8959. }
  8960. public:
  8961. CFilteredGraphIteratorOf<T,U>(T *_base, WUGraphType _type)
  8962. : base(_base), type(_type)
  8963. {
  8964. }
  8965. bool first()
  8966. {
  8967. if (!base->first())
  8968. return false;
  8969. if (match())
  8970. return true;
  8971. return next();
  8972. }
  8973. bool next()
  8974. {
  8975. while (base->next())
  8976. if (match())
  8977. return true;
  8978. return false;
  8979. }
  8980. virtual bool isValid()
  8981. {
  8982. return base->isValid();
  8983. }
  8984. U & query()
  8985. {
  8986. return base->query();
  8987. }
  8988. };
  8989. IConstWUGraphMetaIterator& CLocalWorkUnit::getGraphsMeta(WUGraphType type) const
  8990. {
  8991. /* NB: this method should be 'cheap', loadGraphs() creates IConstWUGraph interfaces to the graphs
  8992. * it does not actually pull the graph data. We only use IConstWUGraphMeta here, which never probes the xgmml.
  8993. */
  8994. CriticalBlock block(crit);
  8995. loadGraphs(false);
  8996. IConstWUGraphMetaIterator *giter = new CArrayIteratorOf<IConstWUGraph,IConstWUGraphMetaIterator> (graphs, 0, (IConstWorkUnit *) this);
  8997. if (type!=GraphTypeAny)
  8998. giter = new CFilteredGraphIteratorOf<IConstWUGraphMetaIterator, IConstWUGraphMeta>(giter,type);
  8999. return *giter;
  9000. }
  9001. IConstWUGraphIterator& CLocalWorkUnit::getGraphs(WUGraphType type) const
  9002. {
  9003. CriticalBlock block(crit);
  9004. loadGraphs(true);
  9005. IConstWUGraphIterator *giter = new CArrayIteratorOf<IConstWUGraph,IConstWUGraphIterator> (graphs, 0, (IConstWorkUnit *) this);
  9006. if (type!=GraphTypeAny)
  9007. giter = new CFilteredGraphIteratorOf<IConstWUGraphIterator, IConstWUGraph>(giter, type);
  9008. return *giter;
  9009. }
  9010. IConstWUGraph* CLocalWorkUnit::getGraph(const char *qname) const
  9011. {
  9012. CriticalBlock block(crit);
  9013. VStringBuffer xpath("Graphs/Graph[@name='%s']", qname);
  9014. // NOTE - this would go wrong if we had other graphs of same name but different type. Ignore for now.
  9015. IPTree *graph = queryMergedTree()->queryPropTree(xpath);
  9016. if (graph)
  9017. return new CLocalWUGraph(*this, LINK(graph));
  9018. return NULL;
  9019. }
  9020. void CLocalWorkUnit::createGraph(const char * name, const char *label, WUGraphType type, IPropertyTree *xgmml, unsigned wfid)
  9021. {
  9022. CriticalBlock block(crit);
  9023. if (!graphs.length())
  9024. p->addPropTree("Graphs");
  9025. IPropertyTree *r = p->queryPropTree("Graphs");
  9026. IPropertyTree *s = r->addPropTree("Graph");
  9027. CLocalWUGraph *q = new CLocalWUGraph(*this, LINK(s));
  9028. q->setName(name);
  9029. q->setLabel(label);
  9030. q->setType(type);
  9031. q->setWfid(wfid);
  9032. q->setXGMMLTree(xgmml);
  9033. graphs.append(*q);
  9034. }
  9035. IConstWUGraphProgress *CLocalWorkUnit::getGraphProgress(const char *graphName) const
  9036. {
  9037. IPTree *graphProgress = p->queryPropTree("GraphProgress");
  9038. if (graphProgress)
  9039. {
  9040. IPTree *progress = graphProgress->queryPropTree(graphName);
  9041. if (progress)
  9042. return new CConstGraphProgress(queryWuid(), graphName, progress);
  9043. }
  9044. return nullptr;
  9045. }
  9046. WUGraphState CLocalWorkUnit::queryGraphState(const char *graphName) const
  9047. {
  9048. throwUnexpected(); // Should only be used for persisted workunits
  9049. }
  9050. WUGraphState CLocalWorkUnit::queryNodeState(const char *graphName, WUGraphIDType nodeId) const
  9051. {
  9052. throwUnexpected(); // Should only be used for persisted workunits
  9053. }
  9054. void CLocalWorkUnit::setGraphState(const char *graphName, unsigned wfid, WUGraphState state) const
  9055. {
  9056. throwUnexpected(); // Should only be used for persisted workunits
  9057. }
  9058. void CLocalWorkUnit::setNodeState(const char *graphName, WUGraphIDType nodeId, WUGraphState state) const
  9059. {
  9060. throwUnexpected(); // Should only be used for persisted workunits
  9061. }
  9062. IWUGraphStats *CLocalWorkUnit::updateStats(const char *graphName, StatisticCreatorType creatorType, const char * creator, unsigned _wfid, unsigned subgraph, bool merge) const
  9063. {
  9064. return new CLocalWuGraphStats(LINK(p), creatorType, creator, _wfid, graphName, subgraph, merge);
  9065. }
  9066. void CLocalWUGraph::setName(const char *str)
  9067. {
  9068. p->setProp("@name", str);
  9069. }
  9070. void CLocalWUGraph::setLabel(const char *str)
  9071. {
  9072. p->setProp("@label", str);
  9073. }
  9074. void CLocalWUGraph::setWfid(unsigned wfid)
  9075. {
  9076. p->setPropInt("@wfid", wfid);
  9077. }
  9078. void CLocalWUGraph::setXGMML(const char *str)
  9079. {
  9080. setXGMMLTree(createPTreeFromXMLString(str,ipt_lowmem));
  9081. }
  9082. void CLocalWUGraph::setXGMMLTree(IPropertyTree *_graph)
  9083. {
  9084. assertex(strcmp(_graph->queryName(), "graph")==0);
  9085. IPropertyTree *xgmml = p->setPropTree("xgmml");
  9086. MemoryBuffer mb;
  9087. _graph->serialize(mb);
  9088. // Note - we could compress further but that would introduce compatibility concerns, so don't bother
  9089. // Cassandra workunit code actually lzw compresses the parent anyway
  9090. xgmml->setPropBin("graphBin", mb.length(), mb.toByteArray());
  9091. graph.setown(_graph);
  9092. }
  9093. static void expandAttributes(IPropertyTree & targetNode, IPropertyTree & progressNode)
  9094. {
  9095. Owned<IAttributeIterator> aIter = progressNode.getAttributes();
  9096. ForEach (*aIter)
  9097. {
  9098. const char *aName = aIter->queryName()+1;
  9099. if (0 != stricmp("id", aName)) // "id" reserved.
  9100. {
  9101. IPropertyTree *att = targetNode.addPropTree("att");
  9102. att->setProp("@name", aName);
  9103. att->setProp("@value", aIter->queryValue());
  9104. }
  9105. }
  9106. }
  9107. void CLocalWUGraph::mergeProgress(IPropertyTree &rootNode, IPropertyTree &progressTree, const unsigned &progressV) const
  9108. {
  9109. IPropertyTree *graphNode = rootNode.queryPropTree("att/graph");
  9110. if (!graphNode) return;
  9111. unsigned nodeId = rootNode.getPropInt("@id");
  9112. StringBuffer progressNodePath("node[@id=\"");
  9113. progressNodePath.append(nodeId).append("\"]");
  9114. IPropertyTree *progressNode = progressTree.queryPropTree(progressNodePath.str());
  9115. if (progressNode)
  9116. {
  9117. expandAttributes(*graphNode, *progressNode);
  9118. Owned<IPropertyTreeIterator> edges = progressNode->getElements("edge");
  9119. ForEach (*edges)
  9120. {
  9121. IPropertyTree &edge = edges->query();
  9122. StringBuffer edgePath("edge[@id=\"");
  9123. edgePath.append(edge.queryProp("@id")).append("\"]");
  9124. IPropertyTree *graphEdge = graphNode->queryPropTree(edgePath.str());
  9125. if (graphEdge)
  9126. {
  9127. if (progressV < 1)
  9128. mergePTree(graphEdge, &edge);
  9129. else
  9130. { // must translate to XGMML format
  9131. expandAttributes(*graphEdge, edge);
  9132. // This is really only here, so that our progress format can use non-attribute values, which have different efficiency qualifies (e.g. can be external by dali)
  9133. Owned<IPropertyTreeIterator> iter = edge.getElements("*");
  9134. ForEach (*iter)
  9135. {
  9136. IPropertyTree &t = iter->query();
  9137. IPropertyTree *att = graphEdge->addPropTree("att");
  9138. att->setProp("@name", t.queryName());
  9139. att->setProp("@value", t.queryProp(NULL));
  9140. }
  9141. }
  9142. }
  9143. }
  9144. Owned<IPropertyTreeIterator> nodes = progressNode->getElements("node");
  9145. ForEach (*nodes)
  9146. {
  9147. IPropertyTree &node = nodes->query();
  9148. StringBuffer nodePath("node[@id=\"");
  9149. nodePath.append(node.queryProp("@id")).append("\"]");
  9150. IPropertyTree *_node = graphNode->queryPropTree(nodePath.str());
  9151. if (_node)
  9152. {
  9153. if (progressV < 1)
  9154. mergePTree(_node, &node);
  9155. else
  9156. { // must translate to XGMML format
  9157. expandAttributes(*_node, node);
  9158. }
  9159. }
  9160. }
  9161. }
  9162. Owned<IPropertyTreeIterator> iter = graphNode->getElements("node");
  9163. ForEach (*iter)
  9164. mergeProgress(iter->query(), progressTree, progressV);
  9165. }
  9166. IPropertyTree * CLocalWUGraph::getXGMMLTreeRaw() const
  9167. {
  9168. return p->getPropTree("xgmml");
  9169. }
  9170. IPropertyTree * CLocalWUGraph::getXGMMLTree(bool doMergeProgress, bool doFormatStats) const
  9171. {
  9172. if (!graph)
  9173. {
  9174. // NB: although graphBin introduced in wuidVersion==2,
  9175. // daliadmin can retrospectively compress existing graphs, so need to check for all versions
  9176. MemoryBuffer mb;
  9177. if (p->getPropBin("xgmml/graphBin", mb))
  9178. graph.setown(createPTree(mb, ipt_lowmem));
  9179. else
  9180. graph.setown(p->getBranch("xgmml/graph"));
  9181. if (!graph)
  9182. return NULL;
  9183. }
  9184. if (!doMergeProgress)
  9185. return graph.getLink();
  9186. else
  9187. {
  9188. Owned<IPropertyTree> copy = createPTreeFromIPT(graph, ipt_lowmem);
  9189. Owned<IConstWUGraphProgress> progress = owner.getGraphProgress(p->queryProp("@name"));
  9190. if (progress)
  9191. {
  9192. //MORE: Eventually this should directly access the new stats structure
  9193. unsigned progressV = progress->queryFormatVersion();
  9194. Owned<IPropertyTree> progressTree = progress->getProgressTree(doFormatStats);
  9195. Owned<IPropertyTreeIterator> nodeIterator = copy->getElements("node");
  9196. ForEach (*nodeIterator)
  9197. mergeProgress(nodeIterator->query(), *progressTree, progressV);
  9198. }
  9199. return copy.getClear();
  9200. }
  9201. }
  9202. WUGraphType CLocalWUGraph::getType() const
  9203. {
  9204. return (WUGraphType) getEnum(p, "@type", graphTypes);
  9205. }
  9206. IStringVal & CLocalWUGraph::getTypeName(IStringVal &str) const
  9207. {
  9208. str.set(p->queryProp("@type"));
  9209. if (!str.length())
  9210. str.set("unknown");
  9211. return str;
  9212. }
  9213. unsigned CLocalWUGraph::getWfid() const
  9214. {
  9215. return p->getPropInt("@wfid", 0);
  9216. }
  9217. void CLocalWUGraph::setType(WUGraphType _type)
  9218. {
  9219. setEnum(p, "@type", _type, graphTypes);
  9220. }
  9221. //=================================================================================================
  9222. EnumMapping queryFileTypes[] = {
  9223. { FileTypeCpp, "cpp" },
  9224. { FileTypeDll, "dll" },
  9225. { FileTypeResText, "res" },
  9226. { FileTypeHintXml, "hint" },
  9227. { FileTypeXml, "xml" },
  9228. { FileTypeLog, "log" },
  9229. { FileTypeSize, NULL },
  9230. };
  9231. CLocalWUAssociated::CLocalWUAssociated(IPropertyTree *props) : p(props)
  9232. {
  9233. }
  9234. WUFileType CLocalWUAssociated::getType() const
  9235. {
  9236. return (WUFileType)getEnum(p, "@type", queryFileTypes);
  9237. }
  9238. IStringVal & CLocalWUAssociated::getDescription(IStringVal & str) const
  9239. {
  9240. str.set(p->queryProp("@desc"));
  9241. return str;
  9242. }
  9243. IStringVal & CLocalWUAssociated::getIp(IStringVal & str) const
  9244. {
  9245. str.set(p->queryProp("@ip"));
  9246. return str;
  9247. }
  9248. IStringVal & CLocalWUAssociated::getName(IStringVal & str) const
  9249. {
  9250. str.set(p->queryProp("@filename"));
  9251. return str;
  9252. }
  9253. IStringVal & CLocalWUAssociated::getNameTail(IStringVal & str) const
  9254. {
  9255. str.set(pathTail(p->queryProp("@filename")));
  9256. return str;
  9257. }
  9258. unsigned CLocalWUAssociated::getCrc() const
  9259. {
  9260. return p->getPropInt("@crc", 0);
  9261. }
  9262. unsigned CLocalWUAssociated::getMinActivityId() const
  9263. {
  9264. return p->getPropInt("@minActivity", 0);
  9265. }
  9266. unsigned CLocalWUAssociated::getMaxActivityId() const
  9267. {
  9268. return p->getPropInt("@maxActivity", 0);
  9269. }
  9270. //=================================================================================================
  9271. CLocalWUQuery::CLocalWUQuery(IPropertyTree *props) : p(props)
  9272. {
  9273. associatedCached = false;
  9274. }
  9275. EnumMapping queryTypes[] = {
  9276. { QueryTypeUnknown, "unknown" },
  9277. { QueryTypeEcl, "ECL" },
  9278. { QueryTypeSql, "SQL" },
  9279. { QueryTypeXml, "XML" },
  9280. { QueryTypeAttribute, "Attribute" },
  9281. { QueryTypeSize, NULL },
  9282. };
  9283. WUQueryType CLocalWUQuery::getQueryType() const
  9284. {
  9285. return (WUQueryType) getEnum(p, "@type", queryTypes);
  9286. }
  9287. void CLocalWUQuery::setQueryType(WUQueryType qt)
  9288. {
  9289. setEnum(p, "@type", qt, queryTypes);
  9290. }
  9291. IStringVal& CLocalWUQuery::getQueryText(IStringVal &str) const
  9292. {
  9293. const char *text = p->queryProp("Text");
  9294. if (!text)
  9295. text = p->queryProp("ShortText");
  9296. str.set(text);
  9297. return str;
  9298. }
  9299. IStringVal& CLocalWUQuery::getQueryShortText(IStringVal &str) const
  9300. {
  9301. const char * text = p->queryProp("ShortText");
  9302. if (text)
  9303. str.set(text);
  9304. else
  9305. {
  9306. text = p->queryProp("Text");
  9307. if (isArchiveQuery(text))
  9308. {
  9309. Owned<IPropertyTree> xml = createPTreeFromXMLString(text, ipt_caseInsensitive|ipt_lowmem);
  9310. const char * path = xml->queryProp("Query/@attributePath");
  9311. if (path)
  9312. {
  9313. IPropertyTree * resolved = resolveDefinitionInArchive(xml, path);
  9314. if (resolved)
  9315. str.set(resolved->queryProp(NULL));
  9316. }
  9317. else
  9318. str.set(xml->queryProp("Query"));
  9319. }
  9320. else
  9321. str.set(text);
  9322. }
  9323. return str;
  9324. }
  9325. bool CLocalWUQuery::isArchive() const
  9326. {
  9327. if (p->hasProp("@isArchive"))
  9328. return p->getPropBool("@isArchive");
  9329. const char *text = p->queryProp("Text");
  9330. return isArchiveQuery(text);
  9331. }
  9332. IStringVal& CLocalWUQuery::getQueryName(IStringVal &str) const
  9333. {
  9334. str.set(p->queryProp("@name"));
  9335. return str;
  9336. }
  9337. IStringVal & CLocalWUQuery::getQueryMainDefinition(IStringVal & str) const
  9338. {
  9339. str.set(p->queryProp("@main"));
  9340. return str;
  9341. }
  9342. IStringVal& CLocalWUQuery::getQueryDllName(IStringVal &str) const
  9343. {
  9344. Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeDll, 0);
  9345. if (entry)
  9346. entry->getNameTail(str);
  9347. return str;
  9348. }
  9349. IStringVal& CLocalWUQuery::getQueryCppName(IStringVal &str) const
  9350. {
  9351. Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeCpp, 0);
  9352. if (entry)
  9353. entry->getName(str);
  9354. return str;
  9355. }
  9356. IStringVal& CLocalWUQuery::getQueryResTxtName(IStringVal &str) const
  9357. {
  9358. Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeResText, 0);
  9359. if (entry)
  9360. entry->getName(str);
  9361. return str;
  9362. }
  9363. unsigned CLocalWUQuery::getQueryDllCrc() const
  9364. {
  9365. Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeDll, 0);
  9366. if (entry)
  9367. return entry->getCrc();
  9368. return 0;
  9369. }
  9370. void CLocalWUQuery::setQueryText(const char *text)
  9371. {
  9372. bool isArchive = isArchiveQuery(text);
  9373. if (isArchive)
  9374. {
  9375. p->setProp("Text", text);
  9376. Owned<IPropertyTree> xml = createPTreeFromXMLString(text, ipt_caseInsensitive|ipt_lowmem);
  9377. const char * path = xml->queryProp("Query/@attributePath");
  9378. if (path)
  9379. {
  9380. IPropertyTree * resolved = resolveDefinitionInArchive(xml, path);
  9381. if (resolved)
  9382. p->setProp("ShortText", resolved->queryProp(NULL));
  9383. }
  9384. else
  9385. p->setProp("ShortText", xml->queryProp("Query"));
  9386. }
  9387. else
  9388. {
  9389. p->setProp("Text", text); // At some point in the future we may be able to remove this,
  9390. // but as long as there may be new workunits compiled by old systems, we can't
  9391. p->setProp("ShortText", text);
  9392. }
  9393. p->setPropBool("@isArchive", isArchive);
  9394. if (isArchive)
  9395. p->setPropBool("@hasArchive", true); //preserved if setQueryText is called multiple times. Should setting this be more explicit?
  9396. }
  9397. void CLocalWUQuery::setQueryName(const char *qname)
  9398. {
  9399. p->setProp("@name", qname);
  9400. }
  9401. void CLocalWUQuery::setQueryMainDefinition(const char * str)
  9402. {
  9403. p->setProp("@main", str);
  9404. }
  9405. void CLocalWUQuery::addAssociatedFile(WUFileType type, const char * name, const char * ip, const char * desc, unsigned crc, unsigned minActivity, unsigned maxActivity)
  9406. {
  9407. CriticalBlock block(crit);
  9408. loadAssociated();
  9409. StringBuffer xpath;
  9410. xpath.append("Associated/File[@filename=\"").append(name).append("\"]");
  9411. if (p->hasProp(xpath))
  9412. return;
  9413. if (!associated.length())
  9414. p->addPropTree("Associated");
  9415. IPropertyTree *pl = p->queryPropTree("Associated");
  9416. IPropertyTree *s = pl->addPropTree("File");
  9417. setEnum(s, "@type", type, queryFileTypes);
  9418. s->setProp("@filename", name);
  9419. s->setProp("@ip", ip);
  9420. s->setProp("@desc", desc);
  9421. if (crc)
  9422. s->setPropInt("@crc", crc);
  9423. if (minActivity)
  9424. s->setPropInt("@minActivity", minActivity);
  9425. if (maxActivity)
  9426. s->setPropInt("@maxActivity", maxActivity);
  9427. IConstWUAssociatedFile * q = new CLocalWUAssociated(LINK(s));
  9428. associated.append(*q);
  9429. }
  9430. void CLocalWUQuery::removeAssociatedFile(WUFileType type, const char * name, const char * desc)
  9431. {
  9432. CriticalBlock block(crit);
  9433. associatedCached = false;
  9434. associated.kill();
  9435. StringBuffer xpath;
  9436. xpath.append("Associated/File");
  9437. if (type)
  9438. xpath.append("[@type=\"").append(getEnumText(type, queryFileTypes)).append("\"]");
  9439. if (name)
  9440. xpath.append("[@filename=\"").append(name).append("\"]");
  9441. if (desc)
  9442. xpath.append("[@desc=\"").append(desc).append("\"]");
  9443. p->removeProp(xpath.str());
  9444. }
  9445. void CLocalWUQuery::removeAssociatedFiles()
  9446. {
  9447. associatedCached = false;
  9448. associated.kill();
  9449. p->removeProp("Associated");
  9450. }
  9451. IConstWUAssociatedFile * CLocalWUQuery::getAssociatedFile(WUFileType type, unsigned index) const
  9452. {
  9453. CriticalBlock block(crit);
  9454. loadAssociated();
  9455. ForEachItemIn(idx, associated)
  9456. {
  9457. CLocalWUAssociated &cur = static_cast<CLocalWUAssociated &>(associated.item(idx));
  9458. if (cur.getType() == type)
  9459. {
  9460. if (index-- == 0)
  9461. return &OLINK(cur);
  9462. }
  9463. }
  9464. return NULL;
  9465. }
  9466. void CLocalWUQuery::addSpecialCaseAssociated(WUFileType type, const char * propname, unsigned crc) const
  9467. {
  9468. const char * name = p->queryProp(propname);
  9469. if (name)
  9470. {
  9471. IPropertyTree *s = createPTree("File");
  9472. setEnum(s, "@type", type, queryFileTypes);
  9473. s->setProp("@filename", name);
  9474. if (crc)
  9475. s->setPropInt("@crc", crc);
  9476. associated.append(*new CLocalWUAssociated(s));
  9477. }
  9478. }
  9479. void CLocalWUQuery::loadAssociated() const
  9480. {
  9481. CriticalBlock block(crit);
  9482. if (!associatedCached)
  9483. {
  9484. assertex(associated.length() == 0);
  9485. addSpecialCaseAssociated(FileTypeDll, "DllName", p->getPropInt("DllCrc", 0));
  9486. addSpecialCaseAssociated(FileTypeCpp, "CppName", 0);
  9487. addSpecialCaseAssociated(FileTypeResText, "ResTxtName", 0);
  9488. Owned<IPropertyTreeIterator> r = p->getElements("Associated/File");
  9489. for (r->first(); r->isValid(); r->next())
  9490. {
  9491. IPropertyTree *rp = &r->query();
  9492. rp->Link();
  9493. associated.append(*new CLocalWUAssociated(rp));
  9494. }
  9495. associatedCached = true;
  9496. }
  9497. }
  9498. IConstWUAssociatedFileIterator& CLocalWUQuery::getAssociatedFiles() const
  9499. {
  9500. CriticalBlock block(crit);
  9501. loadAssociated();
  9502. return *new CArrayIteratorOf<IConstWUAssociatedFile,IConstWUAssociatedFileIterator> (associated, 0, (IConstWUQuery *) this);
  9503. }
  9504. //========================================================================================
  9505. CLocalWUWebServicesInfo::CLocalWUWebServicesInfo(IPropertyTree *props) : p(props)
  9506. {
  9507. }
  9508. IStringVal& CLocalWUWebServicesInfo::getModuleName(IStringVal &str) const
  9509. {
  9510. str.set(p->queryProp("@module"));
  9511. return str;
  9512. }
  9513. IStringVal& CLocalWUWebServicesInfo::getAttributeName(IStringVal &str) const
  9514. {
  9515. str.set(p->queryProp("@attribute"));
  9516. return str;
  9517. }
  9518. IStringVal& CLocalWUWebServicesInfo::getDefaultName(IStringVal &str) const
  9519. {
  9520. str.set(p->queryProp("@defaultName"));
  9521. return str;
  9522. }
  9523. unsigned CLocalWUWebServicesInfo::getWebServicesCRC() const
  9524. {
  9525. return (unsigned) p->getPropInt("@crc");
  9526. }
  9527. IStringVal& CLocalWUWebServicesInfo::getInfo(const char *name, IStringVal &str) const
  9528. {
  9529. if (!name)
  9530. {
  9531. StringBuffer ws_info;
  9532. ws_info.appendf("<%s ", p->queryName());
  9533. Owned<IAttributeIterator> attrs = p->getAttributes();
  9534. for(attrs->first(); attrs->isValid(); attrs->next())
  9535. {
  9536. const char *name = attrs->queryName()+1;
  9537. const char *value = attrs->queryValue();
  9538. ws_info.appendf("%s='%s' ", name, value);
  9539. }
  9540. ws_info.append("> \n");
  9541. Owned<IPropertyTreeIterator> info = p->getElements("*");
  9542. ForEach(*info)
  9543. {
  9544. IPropertyTree &item = info->query();
  9545. const char *name = item.queryName();
  9546. if (name)
  9547. {
  9548. MemoryBuffer mb;
  9549. bool isbin = p->isBinary(name);
  9550. if (isbin)
  9551. {
  9552. p->getPropBin(name,mb);
  9553. if (mb.length())
  9554. {
  9555. unsigned len = 0;
  9556. mb.read(len);
  9557. StringBuffer encodedString;
  9558. StringBuffer val(len, (const char *) mb.readDirect(len));
  9559. encodeXML(val, encodedString);
  9560. ws_info.appendf("<%s>%s</%s>", name, encodedString.str(), name);
  9561. }
  9562. }
  9563. else
  9564. {
  9565. StringBuffer tmp;
  9566. toXML(&item, tmp);
  9567. ws_info.append(tmp.str());
  9568. }
  9569. }
  9570. }
  9571. ws_info.appendf("</%s>", p->queryName());
  9572. str.setLen(ws_info.str(), ws_info.length());
  9573. }
  9574. else
  9575. {
  9576. MemoryBuffer mb;
  9577. p->getPropBin(name,mb);
  9578. if (mb.length())
  9579. {
  9580. unsigned len;
  9581. mb.read(len);
  9582. str.setLen((const char *) mb.readDirect(len), len);
  9583. }
  9584. }
  9585. return str;
  9586. }
  9587. IStringVal& CLocalWUWebServicesInfo::getText(const char *name, IStringVal &str) const
  9588. {
  9589. str.set(p->queryProp(name));
  9590. return str;
  9591. }
  9592. void CLocalWUWebServicesInfo::setModuleName(const char *mname)
  9593. {
  9594. p->setProp("@module", mname);
  9595. }
  9596. void CLocalWUWebServicesInfo::setAttributeName(const char *aname)
  9597. {
  9598. p->setProp("@attribute", aname);
  9599. }
  9600. void CLocalWUWebServicesInfo::setDefaultName(const char *dname)
  9601. {
  9602. p->setProp("@defaultName", dname);
  9603. }
  9604. void CLocalWUWebServicesInfo::setWebServicesCRC(unsigned crc)
  9605. {
  9606. p->setPropInt("@crc", crc);
  9607. }
  9608. void CLocalWUWebServicesInfo::setInfo(const char *name, const char *info)
  9609. {
  9610. MemoryBuffer m;
  9611. unsigned len = (size32_t)strlen(info);
  9612. serializeLPString(len, info, m);
  9613. p->setPropBin(name, m.length(), m.toByteArray());
  9614. }
  9615. void CLocalWUWebServicesInfo::setText(const char *name, const char *info)
  9616. {
  9617. p->setProp(name, info);
  9618. }
  9619. //========================================================================================
  9620. CLocalWUResult::CLocalWUResult(IPropertyTree *props) : p(props)
  9621. {
  9622. }
  9623. EnumMapping resultStatuses[] = {
  9624. { ResultStatusUndefined, "undefined" },
  9625. { ResultStatusCalculated, "calculated" },
  9626. { ResultStatusSupplied, "supplied" },
  9627. { ResultStatusFailed, "failed" },
  9628. { ResultStatusPartial, "partial" },
  9629. { ResultStatusSize, NULL }
  9630. };
  9631. WUResultStatus CLocalWUResult::getResultStatus() const
  9632. {
  9633. return (WUResultStatus ) getEnum(p, "@status", resultStatuses);
  9634. }
  9635. IStringVal& CLocalWUResult::getResultName(IStringVal &str) const
  9636. {
  9637. str.set(p->queryProp("@name"));
  9638. return str;
  9639. }
  9640. int CLocalWUResult::getResultSequence() const
  9641. {
  9642. return p->getPropInt("@sequence", -1);
  9643. }
  9644. bool CLocalWUResult::isResultScalar() const
  9645. {
  9646. return p->getPropInt("@isScalar", 1) != 0;
  9647. }
  9648. bool findSize(int size, IntArray &sizes)
  9649. {
  9650. ForEachItemIn(idx, sizes)
  9651. {
  9652. if (sizes.item(idx)==size)
  9653. return true;
  9654. }
  9655. return false;
  9656. }
  9657. void CLocalWUResult::getSchema(IArrayOf<ITypeInfo> &types, StringAttrArray &names, IStringVal * eclText) const
  9658. {
  9659. MemoryBuffer schema;
  9660. p->getPropBin("SchemaRaw", schema);
  9661. if (schema.length())
  9662. {
  9663. for (;;)
  9664. {
  9665. StringAttr name;
  9666. schema.read(name);
  9667. if (*schema.readDirect(0)==type_void)
  9668. break;
  9669. names.append(*new StringAttrItem(name));
  9670. types.append(*deserializeType(schema)); // MORE - nested records!
  9671. }
  9672. schema.skip(1);
  9673. if (schema.length() != schema.getPos())
  9674. {
  9675. unsigned eclLen;
  9676. schema.read(eclLen);
  9677. const char * schemaData = (const char *)schema.readDirect(eclLen);
  9678. if (eclText)
  9679. {
  9680. eclText->setLen(schemaData, eclLen);
  9681. if ((eclLen == 0) && names.ordinality())
  9682. {
  9683. const char * firstName = names.item(0).text;
  9684. StringBuffer temp;
  9685. temp.append("RECORD ");
  9686. types.item(0).getECLType(temp);
  9687. temp.append(" value{NAMED('").append(firstName).append("')}").append("; END;");
  9688. eclText->set(temp.str());
  9689. }
  9690. }
  9691. }
  9692. }
  9693. }
  9694. void readRow(StringBuffer &out, MemoryBuffer &in, TypeInfoArray &types, StringAttrArray &names)
  9695. {
  9696. ForEachItemIn(idx, types)
  9697. {
  9698. StringAttrItem &name = names.item(idx);
  9699. ITypeInfo &type = types.item(idx);
  9700. unsigned size = type.getSize();
  9701. switch(type.getTypeCode())
  9702. {
  9703. case type_data:
  9704. if (size==UNKNOWN_LENGTH)
  9705. {
  9706. if (in.remaining() < sizeof(int))
  9707. throw MakeStringException(WUERR_CorruptResult, "corrupt workunit information");
  9708. in.read(size);
  9709. }
  9710. outputXmlData(size, in.readDirect(size), name.text, out);
  9711. break;
  9712. case type_string:
  9713. if (size==UNKNOWN_LENGTH)
  9714. {
  9715. if (in.remaining() < sizeof(int))
  9716. throw MakeStringException(WUERR_CorruptResult, "corrupt workunit information");
  9717. in.read(size);
  9718. }
  9719. outputXmlString(size, (const char *) in.readDirect(size), name.text, out);
  9720. break;
  9721. case type_varstring:
  9722. {
  9723. if (size == UNKNOWN_LENGTH)
  9724. size = (size32_t)strlen((const char *) in.readDirect(0))+1;
  9725. const char * text = (const char *) in.readDirect(size);
  9726. outputXmlString((size32_t)strlen(text), text, name.text, out);
  9727. break;
  9728. }
  9729. case type_unicode:
  9730. {
  9731. unsigned len = type.getStringLen();
  9732. if (size==UNKNOWN_LENGTH)
  9733. in.read(len);
  9734. outputXmlUnicode(len, (UChar const *) in.readDirect(len*2), name.text, out);
  9735. }
  9736. break;
  9737. case type_utf8:
  9738. {
  9739. unsigned len = type.getStringLen();
  9740. if (size==UNKNOWN_LENGTH)
  9741. {
  9742. in.read(len);
  9743. size = rtlUtf8Size(len, in.readDirect(0));
  9744. }
  9745. outputXmlUtf8(len, (const char *) in.readDirect(size), name.text, out);
  9746. }
  9747. break;
  9748. case type_qstring:
  9749. {
  9750. unsigned len = type.getStringLen();
  9751. if (size==UNKNOWN_LENGTH)
  9752. in.read(len);
  9753. unsigned outlen;
  9754. char *outstr;
  9755. rtlQStrToStrX(outlen, outstr, len, (const char *) in.readDirect(rtlQStrSize(len)));
  9756. outputXmlString(outlen, outstr, name.text, out);
  9757. free(outstr);
  9758. break;
  9759. }
  9760. case type_int:
  9761. case type_swapint:
  9762. if (type.isSigned())
  9763. {
  9764. const unsigned char *raw = (const unsigned char *) in.readDirect(size);
  9765. unsigned __int64 cval8 = 0;
  9766. //MORE: I think this is wrong - swapped doesn't mean little/big/
  9767. if (type.isSwappedEndian())
  9768. {
  9769. unsigned idx = 0;
  9770. if (raw[idx] & 0x80)
  9771. cval8 = (__int64)-1;
  9772. while (size--)
  9773. cval8 = (cval8 << 8) | raw[idx++];
  9774. }
  9775. else
  9776. {
  9777. if (raw[size-1] & 0x80)
  9778. cval8 = (__int64)-1;
  9779. while (size--)
  9780. cval8 = (cval8 << 8) | raw[size];
  9781. }
  9782. outputXmlInt((__int64) cval8, name.text, out);
  9783. }
  9784. else
  9785. {
  9786. const unsigned char *raw = (const unsigned char *) in.readDirect(size);
  9787. unsigned __int64 cval8 = 0;
  9788. if (type.isSwappedEndian())
  9789. {
  9790. unsigned idx = 0;
  9791. while (size--)
  9792. cval8 = (cval8 << 8) | raw[idx++];
  9793. }
  9794. else
  9795. {
  9796. while (size--)
  9797. cval8 = (cval8 << 8) | raw[size];
  9798. }
  9799. outputXmlUInt(cval8, name.text, out);
  9800. }
  9801. break;
  9802. case type_boolean:
  9803. bool cvalb;
  9804. in.read(cvalb);
  9805. outputXmlBool(cvalb, name.text, out);
  9806. break;
  9807. case type_decimal:
  9808. if (type.isSigned())
  9809. outputXmlDecimal(in.readDirect(size), size, type.getPrecision(), name.text, out);
  9810. else
  9811. outputXmlUDecimal(in.readDirect(size), size, type.getPrecision(), name.text, out);
  9812. break;
  9813. case type_real:
  9814. double cvald;
  9815. switch(size)
  9816. {
  9817. case 4:
  9818. float cvalf;
  9819. in.read(cvalf);
  9820. cvald = cvalf;
  9821. break;
  9822. case 8:
  9823. in.read(cvald);
  9824. break;
  9825. default:
  9826. throwUnexpected();
  9827. }
  9828. outputXmlReal(cvald, name.text, out);
  9829. break;
  9830. default:
  9831. assertex(!"unexpected type in raw record");
  9832. break;
  9833. }
  9834. }
  9835. }
  9836. IStringVal& CLocalWUResult::getResultXml(IStringVal &str, bool hidePassword) const
  9837. {
  9838. TypeInfoArray types;
  9839. StringAttrArray names;
  9840. getSchema(types, names);
  9841. StringBuffer xml;
  9842. const char * name = p->queryProp("@name");
  9843. if (name)
  9844. xml.appendf("<Dataset name=\'%s\'>\n", name);
  9845. else
  9846. xml.append("<Dataset>\n");
  9847. if (hidePassword && p->getPropBool("Format/@password"))
  9848. {
  9849. xml.append(" <Row>");
  9850. appendXMLTag(xml, name, "****");
  9851. xml.append("</Row>\n");
  9852. }
  9853. else if (p->hasProp("Value"))
  9854. {
  9855. MemoryBuffer raw;
  9856. p->getPropBin("Value", raw);
  9857. unsigned __int64 numrows = getResultRowCount();
  9858. while (numrows--)
  9859. {
  9860. xml.append(" <Row>");
  9861. readRow(xml, raw, types, names);
  9862. xml.append("</Row>\n");
  9863. }
  9864. }
  9865. else if (p->hasProp("xmlValue"))
  9866. {
  9867. xml.append(" <Row>");
  9868. appendXMLTag(xml, name, p->queryProp("xmlValue"));
  9869. xml.append("</Row>\n");
  9870. }
  9871. xml.append("</Dataset>\n");
  9872. str.set(xml.str());
  9873. return str;
  9874. }
  9875. IProperties *CLocalWUResult::queryResultXmlns()
  9876. {
  9877. CriticalBlock block(crit);
  9878. if (xmlns)
  9879. return xmlns;
  9880. xmlns.setown(createProperties());
  9881. Owned<IAttributeIterator> it = p->getAttributes();
  9882. unsigned prefixLen = strlen("@xmlns");
  9883. ForEach(*it)
  9884. {
  9885. const char *name = it->queryName();
  9886. if (!strncmp("@xmlns", name, prefixLen))
  9887. {
  9888. if (name[prefixLen]==':') //normal case
  9889. xmlns->setProp(name+prefixLen+1, it->queryValue());
  9890. else if (!name[prefixLen]) //special case, unprefixed namespace
  9891. xmlns->setProp("xmlns", it->queryValue());
  9892. }
  9893. }
  9894. return xmlns;
  9895. }
  9896. unsigned CLocalWUResult::getResultFetchSize() const
  9897. {
  9898. return p->getPropInt("fetchSize", 100);
  9899. }
  9900. __int64 CLocalWUResult::getResultTotalRowCount() const
  9901. {
  9902. return p->getPropInt64("totalRowCount", -1);
  9903. }
  9904. __int64 CLocalWUResult::getResultRowCount() const
  9905. {
  9906. return p->getPropInt64("rowCount", 0);
  9907. }
  9908. void CLocalWUResult::getResultDataset(IStringVal & ecl, IStringVal & defs) const
  9909. {
  9910. ecl.set(p->queryProp("datasetEcl"));
  9911. defs.set(p->queryProp("datasetEclDefs"));
  9912. }
  9913. IStringVal& CLocalWUResult::getResultLogicalName(IStringVal & val) const
  9914. {
  9915. val.set(p->queryProp("logicalName"));
  9916. return val;
  9917. }
  9918. IStringVal& CLocalWUResult::getResultKeyField(IStringVal & ecl) const
  9919. {
  9920. ecl.set(p->queryProp("keyField"));
  9921. return ecl;
  9922. }
  9923. unsigned CLocalWUResult::getResultRequestedRows() const
  9924. {
  9925. return p->getPropInt("requestedRows", 1);
  9926. }
  9927. IStringVal& CLocalWUResult::getResultEclSchema(IStringVal & str) const
  9928. {
  9929. TypeInfoArray types;
  9930. StringAttrArray names;
  9931. getSchema(types, names, &str);
  9932. return str;
  9933. }
  9934. IStringVal& CLocalWUResult::getResultRecordSizeEntry(IStringVal & str) const
  9935. {
  9936. str.set(p->queryProp("@recordSizeEntry"));
  9937. return str;
  9938. }
  9939. IStringVal& CLocalWUResult::getResultTransformerEntry(IStringVal & str) const
  9940. {
  9941. str.set(p->queryProp("@transformerEntry"));
  9942. return str;
  9943. }
  9944. __int64 CLocalWUResult::getResultRowLimit() const
  9945. {
  9946. return p->getPropInt64("@rowLimit");
  9947. }
  9948. IStringVal& CLocalWUResult::getResultFilename(IStringVal & str) const
  9949. {
  9950. str.set(p->queryProp("@tempFilename"));
  9951. return str;
  9952. }
  9953. IStringVal& CLocalWUResult::getResultFieldOpt(const char *name, IStringVal &str) const
  9954. {
  9955. str.clear();
  9956. if (!name || !*name)
  9957. return str;
  9958. IPropertyTree *format = p->queryPropTree("Format");
  9959. if (!format)
  9960. return str;
  9961. VStringBuffer xpath("@%s", name);
  9962. str.set(format->queryProp(xpath));
  9963. return str;
  9964. }
  9965. void CLocalWUResult::getResultWriteLocation(IStringVal & _graph, unsigned & _activityId) const
  9966. {
  9967. _graph.set(p->queryProp("@graph"));
  9968. _activityId = p->getPropInt("@activity", 0);
  9969. }
  9970. void CLocalWUResult::setResultStatus(WUResultStatus status)
  9971. {
  9972. setEnum(p, "@status", status, resultStatuses);
  9973. if (status==ResultStatusUndefined)
  9974. {
  9975. p->removeProp("Value");
  9976. p->removeProp("totalRowCount");
  9977. p->removeProp("rowCount");
  9978. p->removeProp("@format");
  9979. p->removeProp("@tempFileNmae");
  9980. p->removeProp("logicalName");
  9981. }
  9982. }
  9983. void CLocalWUResult::setResultName(const char *s)
  9984. {
  9985. p->setProp("@name", s);
  9986. }
  9987. void CLocalWUResult::setResultSequence(unsigned seq)
  9988. {
  9989. p->setPropInt("@sequence", seq);
  9990. }
  9991. void CLocalWUResult::setResultSchemaRaw(unsigned size, const void *schema)
  9992. {
  9993. p->setPropBin("SchemaRaw", size, schema);
  9994. }
  9995. void CLocalWUResult::setResultXmlns(const char *prefix, const char *uri)
  9996. {
  9997. StringBuffer xpath("@xmlns");
  9998. if (prefix && *prefix)
  9999. xpath.append(':').append(prefix);
  10000. p->setProp(xpath, uri);
  10001. }
  10002. void CLocalWUResult::setResultFieldOpt(const char *name, const char *value)
  10003. {
  10004. if (!name || !*name)
  10005. return;
  10006. IPropertyTree *format = ensurePTree(p, "Format");
  10007. VStringBuffer xpath("@%s", name);
  10008. format->setProp(xpath, value);
  10009. }
  10010. void CLocalWUResult::setResultWriteLocation(const char * _graph, unsigned _activityId)
  10011. {
  10012. p->setProp("@graph", _graph);
  10013. p->setPropInt("@activity", _activityId);
  10014. }
  10015. void CLocalWUResult::setResultScalar(bool isScalar)
  10016. {
  10017. p->setPropInt("@isScalar", (int) isScalar);
  10018. if (isScalar)
  10019. setResultTotalRowCount(1);
  10020. }
  10021. void CLocalWUResult::setResultRaw(unsigned len, const void *data, WUResultFormat format)
  10022. {
  10023. p->setPropBin("Value", len, data);
  10024. setResultStatus(ResultStatusSupplied);
  10025. setResultFormat(format);
  10026. }
  10027. void CLocalWUResult::setResultFormat(WUResultFormat format)
  10028. {
  10029. switch (format)
  10030. {
  10031. case ResultFormatXml:
  10032. p->setProp("@format","xml");
  10033. break;
  10034. case ResultFormatXmlSet:
  10035. p->setProp("@format","xmlSet");
  10036. break;
  10037. case ResultFormatCsv:
  10038. p->setProp("@format","csv");
  10039. break;
  10040. default:
  10041. p->removeProp("@format");
  10042. break;
  10043. }
  10044. }
  10045. void CLocalWUResult::setResultXML(const char *val)
  10046. {
  10047. p->setProp("xmlValue", val);
  10048. }
  10049. void CLocalWUResult::addResultRaw(unsigned len, const void *data, WUResultFormat format)
  10050. {
  10051. p->appendPropBin("Value", len, data);
  10052. setResultStatus(ResultStatusPartial);
  10053. const char *existingFormat = p->queryProp("@format");
  10054. const char *formatStr = NULL;
  10055. switch (format)
  10056. {
  10057. case ResultFormatXml:
  10058. formatStr = "xml";
  10059. break;
  10060. case ResultFormatXmlSet:
  10061. formatStr = "xmlSet";
  10062. break;
  10063. case ResultFormatCsv:
  10064. formatStr = "csv";
  10065. break;
  10066. default:
  10067. existingFormat = nullptr;
  10068. p->removeProp("@format");
  10069. break;
  10070. }
  10071. if (format)
  10072. {
  10073. if (existingFormat)
  10074. {
  10075. if (0 != stricmp(formatStr, existingFormat))
  10076. throw MakeStringException(WUERR_ResultFormatMismatch, "addResult format %s, does not match existing format %s", formatStr, existingFormat);
  10077. }
  10078. else
  10079. p->setProp("@format", formatStr);
  10080. }
  10081. }
  10082. void CLocalWUResult::setResultFetchSize(unsigned rows)
  10083. {
  10084. p->setPropInt("fetchSize", rows);
  10085. }
  10086. void CLocalWUResult::setResultTotalRowCount(__int64 rows)
  10087. {
  10088. p->setPropInt64("totalRowCount", rows);
  10089. }
  10090. void CLocalWUResult::setResultRowCount(__int64 rows)
  10091. {
  10092. p->setPropInt64("rowCount", rows);
  10093. }
  10094. void CLocalWUResult::setResultDataset(const char *ecl, const char *defs)
  10095. {
  10096. p->setProp("datasetEcl", ecl);
  10097. p->setProp("datasetEclDefs", defs);
  10098. }
  10099. void CLocalWUResult::setResultLogicalName(const char *logicalName)
  10100. {
  10101. p->setProp("logicalName", logicalName);
  10102. }
  10103. void CLocalWUResult::setResultKeyField(const char *ecl)
  10104. {
  10105. p->setProp("keyField", ecl);
  10106. }
  10107. void CLocalWUResult::setResultRequestedRows(unsigned rows)
  10108. {
  10109. p->setPropInt("requestedRows", rows);
  10110. }
  10111. void CLocalWUResult::setResultRecordSizeEntry(const char * entry)
  10112. {
  10113. p->setProp("@recordSizeEntry", entry);
  10114. }
  10115. void CLocalWUResult::setResultTransformerEntry(const char * entry)
  10116. {
  10117. p->setProp("@transformerEntry", entry);
  10118. }
  10119. void CLocalWUResult::setResultRowLimit(__int64 value)
  10120. {
  10121. p->setPropInt64("@rowLimit", value);
  10122. }
  10123. void CLocalWUResult::setResultFilename(const char * name)
  10124. {
  10125. p->setProp("@tempFilename", name);
  10126. }
  10127. // MORE - it's an undetected error if we call getResult... of a type that does not match schema
  10128. __int64 CLocalWUResult::getResultInt() const
  10129. {
  10130. __int64 result = 0;
  10131. MemoryBuffer s;
  10132. p->getPropBin("Value", s);
  10133. if (s.length())
  10134. s.read(result);
  10135. else
  10136. {
  10137. // NOTE - we use this rather than getPropInt64 since it handles uint64 values up to MAX_UINT better (for our purposes)
  10138. const char *val = p->queryProp("xmlValue");
  10139. if (val)
  10140. result = rtlStrToInt8(strlen(val), val);
  10141. }
  10142. return result;
  10143. }
  10144. bool CLocalWUResult::getResultBool() const
  10145. {
  10146. bool result = false;
  10147. MemoryBuffer s;
  10148. p->getPropBin("Value", s);
  10149. if (s.length())
  10150. s.read(result);
  10151. else
  10152. result = p->getPropBool("xmlValue");
  10153. return result;
  10154. }
  10155. double CLocalWUResult::getResultReal() const
  10156. {
  10157. double result = 0;
  10158. MemoryBuffer s;
  10159. p->getPropBin("Value", s);
  10160. if (s.length())
  10161. s.read(result);
  10162. else
  10163. {
  10164. const char *xmlVal = p->queryProp("xmlValue");
  10165. if (xmlVal)
  10166. result = atof(xmlVal);
  10167. }
  10168. return result;
  10169. }
  10170. void CLocalWUResult::getResultDecimal(void * val, unsigned len, unsigned precision, bool isSigned) const
  10171. {
  10172. MemoryBuffer s;
  10173. p->getPropBin("Value", s);
  10174. if (s.length())
  10175. {
  10176. assertex(s.length() == len);
  10177. s.read(len, val);
  10178. }
  10179. else
  10180. {
  10181. const char *xmlVal = p->queryProp("xmlValue");
  10182. if (xmlVal)
  10183. {
  10184. Decimal d;
  10185. d.setString(strlen(xmlVal), xmlVal);
  10186. if (isSigned)
  10187. d.getDecimal(len, precision, val);
  10188. else
  10189. d.getUDecimal(len, precision, val);
  10190. }
  10191. else
  10192. memset(val, 0, len);
  10193. }
  10194. }
  10195. IStringVal& CLocalWUResult::getResultString(IStringVal & str, bool hidePassword) const
  10196. {
  10197. if (hidePassword && p->getPropBool("@password"))
  10198. {
  10199. str.set("****");
  10200. return str;
  10201. }
  10202. MemoryBuffer s;
  10203. p->getPropBin("Value", s);
  10204. if (s.length())
  10205. {
  10206. unsigned len;
  10207. s.read(len);
  10208. str.setLen((const char *) s.readDirect(len), len);
  10209. }
  10210. else
  10211. {
  10212. p->getPropBin("xmlValue", s);
  10213. if (p->isBinary("xmlValue"))
  10214. str.setLen(s.toByteArray(), s.length());
  10215. else
  10216. {
  10217. char *ascii = rtlUtf8ToVStr(rtlUtf8Length(s.length(), s.toByteArray()), s.toByteArray());
  10218. str.set(ascii);
  10219. rtlFree(ascii);
  10220. }
  10221. }
  10222. return str;
  10223. }
  10224. WUResultFormat CLocalWUResult::getResultFormat() const
  10225. {
  10226. const char * format = p->queryProp("@format");
  10227. if (!format)
  10228. return ResultFormatRaw;
  10229. else if (strcmp(format, "xml") == 0)
  10230. return ResultFormatXml;
  10231. else if (strcmp(format, "xmlSet") == 0)
  10232. return ResultFormatXmlSet;
  10233. else if (strcmp(format, "csv") == 0)
  10234. return ResultFormatCsv;
  10235. else
  10236. throw MakeStringException(WUERR_InvalidResultFormat, "Unrecognised result format %s", format);
  10237. }
  10238. IDataVal& CLocalWUResult::getResultRaw(IDataVal & data, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const
  10239. {
  10240. MemoryBuffer s;
  10241. p->getPropBin("Value", s);
  10242. unsigned len = s.length();
  10243. if (len)
  10244. {
  10245. WUResultFormat format = getResultFormat();
  10246. if (format == ResultFormatXml || format == ResultFormatXmlSet)
  10247. {
  10248. if (!xmlTransformer)
  10249. throw MakeStringException(WUERR_MissingFormatTranslator, "No transformer supplied to translate XML format result");
  10250. xmlTransformer->transform(data, len, s.readDirect(len), format == ResultFormatXml);
  10251. }
  10252. else if (format == ResultFormatCsv)
  10253. {
  10254. if (!csvTransformer)
  10255. throw MakeStringException(WUERR_MissingFormatTranslator, "No transformer supplied to translate Csv format result");
  10256. csvTransformer->transform(data, len, s.readDirect(len), true);
  10257. }
  10258. else
  10259. data.setLen(s.readDirect(len), len);
  10260. }
  10261. else
  10262. data.clear();
  10263. return data;
  10264. }
  10265. unsigned CLocalWUResult::getResultHash() const
  10266. {
  10267. MemoryBuffer s;
  10268. p->getPropBin("Value", s);
  10269. unsigned len = s.length();
  10270. const byte * data = (const byte *)s.toByteArray();
  10271. return ~hashc(data, len, ~0);
  10272. }
  10273. IDataVal& CLocalWUResult::getResultUnicode(IDataVal & data) const
  10274. {
  10275. MemoryBuffer s;
  10276. p->getPropBin("Value", s);
  10277. if (s.length())
  10278. {
  10279. unsigned len;
  10280. s.read(len);
  10281. data.setLen(s.readDirect(len*2), len*2);
  10282. }
  10283. else
  10284. {
  10285. StringBuffer utf8;
  10286. if (p->getProp("xmlValue", utf8))
  10287. {
  10288. unsigned outlen;
  10289. UChar *out;
  10290. rtlUtf8ToUnicodeX(outlen, out, utf8.length(), utf8.str());
  10291. data.setLen(out, outlen*2);
  10292. rtlFree(out);
  10293. }
  10294. else
  10295. data.clear();
  10296. }
  10297. return data;
  10298. }
  10299. __int64 CLocalWUResult::getResultRawSize(IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const
  10300. {
  10301. WUResultFormat format = getResultFormat();
  10302. if (format == ResultFormatRaw)
  10303. {
  10304. //MORE: This should not load the whole property...
  10305. MemoryBuffer s;
  10306. p->getPropBin("Value", s);
  10307. return s.length();
  10308. }
  10309. else
  10310. {
  10311. MemoryBuffer temp;
  10312. MemoryBuffer2IDataVal adaptor(temp);
  10313. getResultRaw(adaptor, xmlTransformer, csvTransformer);
  10314. return temp.length();
  10315. }
  10316. }
  10317. IDataVal& CLocalWUResult::getResultRaw(IDataVal & data, __int64 from, __int64 length, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const
  10318. {
  10319. WUResultFormat format = getResultFormat();
  10320. if (format != ResultFormatRaw)
  10321. {
  10322. MemoryBuffer temp;
  10323. MemoryBuffer2IDataVal adaptor(temp);
  10324. getResultRaw(adaptor, xmlTransformer, csvTransformer);
  10325. unsigned len = temp.length();
  10326. if (from > len) from = len;
  10327. if (from + length > len) length = len - from;
  10328. data.setLen(temp.readDirect(len) + from, (size32_t)length);
  10329. return data;
  10330. }
  10331. else
  10332. {
  10333. //MORE: This should not load the whole property, and should be different from the code above...
  10334. MemoryBuffer s;
  10335. p->getPropBin("Value", s);
  10336. unsigned len = s.length();
  10337. if (from > len) from = len;
  10338. if (from + length > len) length = len - from;
  10339. data.setLen(s.readDirect(len) + from, (size32_t)length);
  10340. return data;
  10341. }
  10342. }
  10343. bool CLocalWUResult::getResultIsAll() const
  10344. {
  10345. return p->getPropBool("@isAll", false);
  10346. }
  10347. // MORE - it's an undetected error if we call setResult... of a type that does not match schema
  10348. void CLocalWUResult::setResultInt(__int64 val)
  10349. {
  10350. // Note: we always serialize scalar integer results as int8, and schema must reflect this
  10351. MemoryBuffer m;
  10352. serializeInt8(val, m);
  10353. p->setPropBin("Value", m.length(), m.toByteArray());
  10354. setResultRowCount(1);
  10355. setResultTotalRowCount(1);
  10356. }
  10357. void CLocalWUResult::setResultUInt(unsigned __int64 val)
  10358. {
  10359. setResultInt((__int64) val);
  10360. }
  10361. void CLocalWUResult::setResultReal(double val)
  10362. {
  10363. // Note: we always serialize scalar real results as real8, and schema must reflect this
  10364. MemoryBuffer m;
  10365. serializeReal8(val, m);
  10366. p->setPropBin("Value", m.length(), m.toByteArray());
  10367. setResultRowCount(1);
  10368. setResultTotalRowCount(1);
  10369. }
  10370. void CLocalWUResult::setResultBool(bool val)
  10371. {
  10372. MemoryBuffer m;
  10373. serializeBool(val, m);
  10374. p->setPropBin("Value", m.length(), m.toByteArray());
  10375. setResultRowCount(1);
  10376. setResultTotalRowCount(1);
  10377. }
  10378. void CLocalWUResult::setResultString(const char *val, unsigned len)
  10379. {
  10380. // Note: we always serialize scalar strings with length prefix, and schema must reflect this
  10381. MemoryBuffer m;
  10382. serializeLPString(len, val, m);
  10383. p->setPropBin("Value", m.length(), m.toByteArray());
  10384. setResultRowCount(1);
  10385. setResultTotalRowCount(1);
  10386. }
  10387. void CLocalWUResult::setResultUnicode(const void *val, unsigned len)
  10388. {
  10389. // Note: we always serialize scalar strings with length prefix, and schema must reflect this
  10390. MemoryBuffer m;
  10391. m.append(len).append(len*2, val);
  10392. p->setPropBin("Value", m.length(), m.toByteArray());
  10393. setResultRowCount(1);
  10394. setResultTotalRowCount(1);
  10395. }
  10396. void CLocalWUResult::setResultData(const void *val, unsigned len)
  10397. {
  10398. // Note: we always serialize scalar data with length prefix, and schema must reflect this
  10399. MemoryBuffer m;
  10400. serializeLPString(len, (const char *)val, m);
  10401. p->setPropBin("Value", m.length(), m.toByteArray());
  10402. setResultRowCount(1);
  10403. setResultTotalRowCount(1);
  10404. }
  10405. void CLocalWUResult::setResultDecimal(const void *val, unsigned len)
  10406. {
  10407. // Note: serialized as data but with length known from schema
  10408. MemoryBuffer m;
  10409. serializeFixedData(len, val, m);
  10410. p->setPropBin("Value", m.length(), m.toByteArray());
  10411. setResultRowCount(1);
  10412. setResultTotalRowCount(1);
  10413. }
  10414. void CLocalWUResult::setResultRow(unsigned len, const void * data)
  10415. {
  10416. p->setPropBin("Value", len, data);
  10417. setResultRowCount(1);
  10418. setResultTotalRowCount(1);
  10419. setResultFormat(ResultFormatRaw);
  10420. }
  10421. void CLocalWUResult::setResultIsAll(bool value)
  10422. {
  10423. p->setPropBool("@isAll", value);
  10424. }
  10425. //==========================================================================================
  10426. CLocalWUPlugin::CLocalWUPlugin(IPropertyTree *props) : p(props)
  10427. {
  10428. }
  10429. IStringVal& CLocalWUPlugin::getPluginName(IStringVal &str) const
  10430. {
  10431. str.set(p->queryProp("@dllname"));
  10432. return str;
  10433. }
  10434. IStringVal& CLocalWUPlugin::getPluginVersion(IStringVal &str) const
  10435. {
  10436. str.set(p->queryProp("@version"));
  10437. return str;
  10438. }
  10439. void CLocalWUPlugin::setPluginName(const char *str)
  10440. {
  10441. p->setProp("@dllname", str);
  10442. }
  10443. void CLocalWUPlugin::setPluginVersion(const char *str)
  10444. {
  10445. p->setProp("@version", str);
  10446. }
  10447. //==========================================================================================
  10448. CLocalWULibrary::CLocalWULibrary(IPropertyTree *props) : p(props)
  10449. {
  10450. }
  10451. IStringVal& CLocalWULibrary::getName(IStringVal &str) const
  10452. {
  10453. str.set(p->queryProp("@name"));
  10454. return str;
  10455. }
  10456. void CLocalWULibrary::setName(const char *str)
  10457. {
  10458. p->setProp("@name", str);
  10459. }
  10460. //==========================================================================================
  10461. CLocalWUException::CLocalWUException(IPropertyTree *props) : p(props)
  10462. {
  10463. }
  10464. IStringVal& CLocalWUException::getExceptionSource(IStringVal &str) const
  10465. {
  10466. str.set(p->queryProp("@source"));
  10467. return str;
  10468. }
  10469. IStringVal& CLocalWUException::getExceptionMessage(IStringVal &str) const
  10470. {
  10471. str.set(p->queryProp(NULL));
  10472. return str;
  10473. }
  10474. unsigned CLocalWUException::getExceptionCode() const
  10475. {
  10476. return p->getPropInt("@code", 0);
  10477. }
  10478. ErrorSeverity CLocalWUException::getSeverity() const
  10479. {
  10480. return (ErrorSeverity)p->getPropInt("@severity", SeverityError);
  10481. }
  10482. IStringVal & CLocalWUException::getTimeStamp(IStringVal & dt) const
  10483. {
  10484. dt.set(p->queryProp("@time"));
  10485. return dt;
  10486. }
  10487. IStringVal & CLocalWUException::getExceptionFileName(IStringVal & str) const
  10488. {
  10489. str.set(p->queryProp("@filename"));
  10490. return str;
  10491. }
  10492. unsigned CLocalWUException::getExceptionLineNo() const
  10493. {
  10494. return p->getPropInt("@row", 0);
  10495. }
  10496. unsigned CLocalWUException::getExceptionColumn() const
  10497. {
  10498. return p->getPropInt("@col", 0);
  10499. }
  10500. unsigned CLocalWUException::getActivityId() const
  10501. {
  10502. const char * scope = queryScope();
  10503. if (scope)
  10504. {
  10505. const char * colon = strrchr(scope, ':');
  10506. if (colon && hasPrefix(colon+1, ActivityScopePrefix, true))
  10507. return atoi(colon+1+strlen(ActivityScopePrefix));
  10508. }
  10509. return p->getPropInt("@activity", 0);
  10510. }
  10511. unsigned CLocalWUException::getSequence() const
  10512. {
  10513. return p->getPropInt("@sequence", 0);
  10514. }
  10515. const char * CLocalWUException::queryScope() const
  10516. {
  10517. return p->queryProp("@scope");
  10518. }
  10519. unsigned CLocalWUException::getPriority() const
  10520. {
  10521. return p->getPropInt("@prio", 0);
  10522. }
  10523. void CLocalWUException::setExceptionSource(const char *str)
  10524. {
  10525. p->setProp("@source", str);
  10526. }
  10527. void CLocalWUException::setExceptionMessage(const char *str)
  10528. {
  10529. p->setProp(NULL, str);
  10530. }
  10531. void CLocalWUException::setExceptionCode(unsigned code)
  10532. {
  10533. p->setPropInt("@code", code);
  10534. }
  10535. void CLocalWUException::setSeverity(ErrorSeverity level)
  10536. {
  10537. p->setPropInt("@severity", level);
  10538. }
  10539. void CLocalWUException::setTimeStamp(const char *str)
  10540. {
  10541. p->setProp("@time", str);
  10542. }
  10543. void CLocalWUException::setExceptionFileName(const char *str)
  10544. {
  10545. p->setProp("@filename", str);
  10546. }
  10547. void CLocalWUException::setExceptionLineNo(unsigned r)
  10548. {
  10549. p->setPropInt("@row", r);
  10550. }
  10551. void CLocalWUException::setExceptionColumn(unsigned c)
  10552. {
  10553. p->setPropInt("@col", c);
  10554. }
  10555. void CLocalWUException::setActivityId(unsigned _id)
  10556. {
  10557. p->setPropInt("@activity", _id);
  10558. }
  10559. void CLocalWUException::setScope(const char * _scope)
  10560. {
  10561. p->setProp("@scope", _scope);
  10562. }
  10563. void CLocalWUException::setPriority(unsigned _priority)
  10564. {
  10565. p->setPropInt("@prio", _priority);
  10566. }
  10567. //==========================================================================================
  10568. CLocalWUAppValue::CLocalWUAppValue(const IPropertyTree *_owner, const IPropertyTree *_props) : owner(_owner), props(_props)
  10569. {
  10570. }
  10571. const char * CLocalWUAppValue::queryApplication() const
  10572. {
  10573. return owner->queryName();
  10574. }
  10575. const char * CLocalWUAppValue::queryName() const
  10576. {
  10577. return props->queryName();
  10578. }
  10579. const char * CLocalWUAppValue::queryValue() const
  10580. {
  10581. return props->queryProp(nullptr);
  10582. }
  10583. //==========================================================================================
  10584. CLocalWUStatistic::CLocalWUStatistic(const IPropertyTree *props) : p(props)
  10585. {
  10586. }
  10587. IStringVal & CLocalWUStatistic::getCreator(IStringVal & str) const
  10588. {
  10589. const char * creator = p->queryProp("@creator");
  10590. str.set(creator);
  10591. return str;
  10592. }
  10593. IStringVal & CLocalWUStatistic::getDescription(IStringVal & str, bool createDefault) const
  10594. {
  10595. const char * desc = p->queryProp("@desc");
  10596. if (desc)
  10597. {
  10598. str.set(desc); // legacy and in case it is overridden
  10599. }
  10600. else if (createDefault)
  10601. {
  10602. StatisticKind kind = getKind();
  10603. assertex(kind != StKindNone);
  10604. const char * scope = p->queryProp("@scope");
  10605. assertex(scope);
  10606. //Clean up the format of the scope when converting it to a description
  10607. StringBuffer descriptionText;
  10608. if (isGlobalScope(scope))
  10609. {
  10610. const char * creator = p->queryProp("@creator");
  10611. descriptionText.append(creator).append(":");
  10612. queryLongStatisticName(descriptionText, kind);
  10613. }
  10614. else
  10615. {
  10616. for (;;)
  10617. {
  10618. char c = *scope++;
  10619. if (!c)
  10620. break;
  10621. if (c == ':')
  10622. descriptionText.append(": ");
  10623. else
  10624. descriptionText.append(c);
  10625. }
  10626. if (kind != StTimeElapsed)
  10627. queryLongStatisticName(descriptionText.append(": "), kind);
  10628. }
  10629. str.set(descriptionText);
  10630. }
  10631. else
  10632. str.clear();
  10633. return str;
  10634. }
  10635. IStringVal & CLocalWUStatistic::getFormattedValue(IStringVal & str) const
  10636. {
  10637. StringBuffer formatted;
  10638. formatStatistic(formatted, getValue(), getMeasure());
  10639. str.set(formatted);
  10640. return str;
  10641. }
  10642. StatisticCreatorType CLocalWUStatistic::getCreatorType() const
  10643. {
  10644. return queryCreatorType(p->queryProp("@c"), SCTnone);
  10645. }
  10646. StatisticScopeType CLocalWUStatistic::getScopeType() const
  10647. {
  10648. return queryScopeType(p->queryProp("@s"), SSTnone);
  10649. }
  10650. StatisticKind CLocalWUStatistic::getKind() const
  10651. {
  10652. return queryStatisticKind(p->queryProp("@kind"), StKindNone);
  10653. }
  10654. const char * CLocalWUStatistic::queryScope() const
  10655. {
  10656. const char * scope = p->queryProp("@scope");
  10657. if (!scope || streq(scope, LEGACY_GLOBAL_SCOPE))
  10658. scope = GLOBAL_SCOPE;
  10659. return scope;
  10660. }
  10661. StatisticMeasure CLocalWUStatistic::getMeasure() const
  10662. {
  10663. return queryMeasure(p->queryProp("@unit"), SMeasureNone);
  10664. }
  10665. unsigned __int64 CLocalWUStatistic::getValue() const
  10666. {
  10667. return p->getPropInt64("@value", 0);
  10668. }
  10669. unsigned __int64 CLocalWUStatistic::getCount() const
  10670. {
  10671. return p->getPropInt64("@count", 0);
  10672. }
  10673. unsigned __int64 CLocalWUStatistic::getMax() const
  10674. {
  10675. return p->getPropInt64("@max", 0);
  10676. }
  10677. unsigned __int64 CLocalWUStatistic::getTimestamp() const
  10678. {
  10679. return p->getPropInt64("@ts", 0);
  10680. }
  10681. //==========================================================================================
  10682. extern WORKUNIT_API ILocalWorkUnit * createLocalWorkUnit(const char *xml)
  10683. {
  10684. Owned<CLocalWorkUnit> cw = new CLocalWorkUnit((ISecManager *) NULL, NULL);
  10685. if (xml)
  10686. cw->loadPTree(createPTreeFromXMLString(xml, ipt_lowmem));
  10687. else
  10688. {
  10689. Owned<IPropertyTree> p = createPTree("W_LOCAL", ipt_lowmem);
  10690. p->setProp("@xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance");
  10691. cw->loadPTree(p.getClear());
  10692. }
  10693. ILocalWorkUnit* ret = QUERYINTERFACE(&cw->lockRemote(false), ILocalWorkUnit);
  10694. return ret;
  10695. }
  10696. extern WORKUNIT_API ILocalWorkUnit * createLocalWorkUnitFromPTree(IPropertyTree *ptree)
  10697. {
  10698. Owned<CLocalWorkUnit> cw = new CLocalWorkUnit((ISecManager *) NULL, NULL);
  10699. cw->loadPTree(ptree);
  10700. ILocalWorkUnit* ret = QUERYINTERFACE(&cw->lockRemote(false), ILocalWorkUnit);
  10701. return ret;
  10702. }
  10703. void exportWorkUnitToXMLWithHiddenPasswords(IPropertyTree *p, IIOStream &out, unsigned extraXmlFlags)
  10704. {
  10705. const char *name = p->queryName();
  10706. if (!name)
  10707. name = "__unnamed__";
  10708. StringBuffer temp;
  10709. writeStringToStream(out, appendPTreeOpenTag(temp, p, name, 1, true));
  10710. Owned<IPropertyTreeIterator> elems = p->getElements("*", iptiter_sort);
  10711. ForEach(*elems)
  10712. {
  10713. IPropertyTree &elem = elems->query();
  10714. if (streq(elem.queryName(), "Parameters"))
  10715. {
  10716. writeStringToStream(out, appendPTreeOpenTag(temp.clear().append(' '), &elem, "Parameters", 2, false).append('\n'));
  10717. Owned<IPropertyTreeIterator> params = elem.getElements("*", iptiter_sort);
  10718. ForEach(*params)
  10719. {
  10720. IPropertyTree &param = params->query();
  10721. const char *paramname = param.queryName();
  10722. VStringBuffer xpath("Variables/Variable[@name='%s']/Format/@password", paramname);
  10723. if (p->getPropBool(xpath))
  10724. writeStringToStream(out, appendXMLTag(temp.clear().append(" "), paramname, "****").append('\n'));
  10725. else
  10726. {
  10727. toXML(&param, out, 2, XML_Format|XML_SortTags|extraXmlFlags);
  10728. }
  10729. }
  10730. writeStringToStream(out, appendXMLCloseTag(temp.clear().append(' '), "Parameters").append('\n'));
  10731. }
  10732. else if (streq(elem.queryName(), "Variables"))
  10733. {
  10734. writeStringToStream(out, appendPTreeOpenTag(temp.clear().append(' '), &elem, "Variables", 2, false).append('\n'));
  10735. Owned<IPropertyTreeIterator> vars = elem.getElements("*", iptiter_sort);
  10736. ForEach(*vars)
  10737. {
  10738. Owned<IPropertyTree> var = LINK(&vars->query());
  10739. if (var->getPropBool("Format/@password"))
  10740. {
  10741. var.setown(createPTreeFromIPT(var)); //copy and remove password values
  10742. var->removeProp("Value");
  10743. var->removeProp("xmlValue");
  10744. }
  10745. toXML(var, out, 2, XML_Format|XML_SortTags|extraXmlFlags);
  10746. }
  10747. writeStringToStream(out, appendXMLCloseTag(temp.clear().append(' '), "Variables").append('\n'));
  10748. }
  10749. else
  10750. toXML(&elem, out, 1, XML_Format|XML_SortTags|extraXmlFlags);
  10751. }
  10752. writeStringToStream(out, appendXMLCloseTag(temp.clear(), name));
  10753. }
  10754. StringBuffer &exportWorkUnitToXMLWithHiddenPasswords(IPropertyTree *p, StringBuffer &str)
  10755. {
  10756. class CAdapter : public CInterface, implements IIOStream
  10757. {
  10758. StringBuffer &out;
  10759. public:
  10760. IMPLEMENT_IINTERFACE;
  10761. CAdapter(StringBuffer &_out) : out(_out) { }
  10762. virtual void flush() { }
  10763. virtual size32_t read(size32_t len, void * data) { UNIMPLEMENTED; return 0; }
  10764. virtual size32_t write(size32_t len, const void * data) { out.append(len, (const char *)data); return len; }
  10765. } adapter(str);
  10766. exportWorkUnitToXMLWithHiddenPasswords(p->queryBranch(NULL), adapter, 0);
  10767. return str;
  10768. }
  10769. void exportWorkUnitToXMLFileWithHiddenPasswords(IPropertyTree *p, const char *filename, unsigned extraXmlFlags)
  10770. {
  10771. OwnedIFile ifile = createIFile(filename);
  10772. OwnedIFileIO ifileio = ifile->open(IFOcreate);
  10773. Owned<IIOStream> stream = createIOStream(ifileio);
  10774. exportWorkUnitToXMLWithHiddenPasswords(p->queryBranch(NULL), *stream, extraXmlFlags);
  10775. }
  10776. extern WORKUNIT_API StringBuffer &exportWorkUnitToXML(const IConstWorkUnit *wu, StringBuffer &str, bool unpack, bool includeProgress, bool hidePasswords)
  10777. {
  10778. // MORE - queryPTree isn't really safe without holding CLocalWorkUnit::crit - really need to move these functions into CLocalWorkunit
  10779. const IExtendedWUInterface *ewu = queryExtendedWU(wu);
  10780. if (ewu)
  10781. {
  10782. Linked<IPropertyTree> p;
  10783. if (unpack||includeProgress)
  10784. p.setown(ewu->getUnpackedTree(includeProgress));
  10785. else
  10786. p.set(ewu->queryPTree());
  10787. if (hidePasswords)
  10788. return exportWorkUnitToXMLWithHiddenPasswords(p, str);
  10789. else
  10790. return toXML(p, str, 0, XML_Format|XML_SortTags);
  10791. }
  10792. else
  10793. return str.append("Unrecognized workunit format");
  10794. }
  10795. extern WORKUNIT_API void exportWorkUnitToXMLFile(const IConstWorkUnit *wu, const char * filename, unsigned extraXmlFlags, bool unpack, bool includeProgress, bool hidePasswords, bool regressionTest)
  10796. {
  10797. const IExtendedWUInterface *ewu = queryExtendedWU(wu);
  10798. if (ewu)
  10799. {
  10800. Linked<IPropertyTree> p;
  10801. if (unpack||includeProgress)
  10802. p.setown(ewu->getUnpackedTree(includeProgress));
  10803. else if (regressionTest)
  10804. p.setown(createPTreeFromIPT(ewu->queryPTree()));
  10805. else
  10806. p.set(ewu->queryPTree());
  10807. if (hidePasswords)
  10808. return exportWorkUnitToXMLFileWithHiddenPasswords(p, filename, extraXmlFlags);
  10809. if (regressionTest)
  10810. {
  10811. //This removes any items from the xml that will vary from run to run, so they can be binary compared from run to run
  10812. //The following attributes change with the build. Simpler to remove rather than needing to updated each build
  10813. p->removeProp("@buildVersion");
  10814. p->removeProp("@eclVersion");
  10815. p->removeProp("@hash");
  10816. //Remove statistics, and extract them to a separate file
  10817. IPropertyTree * stats = p->queryPropTree("Statistics");
  10818. if (stats)
  10819. {
  10820. StringBuffer statsFilename;
  10821. statsFilename.append(filename).append(".stats");
  10822. saveXML(statsFilename, stats, 0, (XML_Format|XML_SortTags|extraXmlFlags) & ~XML_LineBreakAttributes);
  10823. p->removeProp("Statistics");
  10824. }
  10825. //Now remove timestamps from exceptions
  10826. Owned<IPropertyTreeIterator> elems = p->getElements("Exceptions/Exception", iptiter_sort);
  10827. ForEach(*elems)
  10828. {
  10829. IPropertyTree &elem = elems->query();
  10830. elem.removeProp("@time");
  10831. }
  10832. }
  10833. saveXML(filename, p, 0, XML_Format|XML_SortTags|extraXmlFlags);
  10834. }
  10835. else
  10836. throw makeStringException(0, "Unrecognized workunit format");
  10837. }
  10838. extern WORKUNIT_API void submitWorkUnit(const char *wuid, const char *username, const char *password)
  10839. {
  10840. MemoryBuffer buffer;
  10841. Owned<INamedQueueConnection> conn = createNamedQueueConnection(0); // MORE - security token?
  10842. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  10843. Owned<IConstWorkUnit> cw = factory->openWorkUnit(wuid);
  10844. assertex(cw);
  10845. StringAttr clusterName(cw->queryClusterName());
  10846. cw.clear();
  10847. if (!clusterName.length())
  10848. throw MakeStringException(WUERR_InvalidCluster, "No target cluster specified");
  10849. StringBuffer serverQueue;
  10850. getClusterEclCCServerQueueName(serverQueue, clusterName);
  10851. assertex(serverQueue.length());
  10852. Owned<IJobQueue> queue = createJobQueue(serverQueue.str());
  10853. if (!queue.get())
  10854. throw MakeStringException(WUERR_InvalidQueue, "Could not create workunit queue");
  10855. IJobQueueItem *item = createJobQueueItem(wuid);
  10856. queue->enqueue(item);
  10857. }
  10858. extern WORKUNIT_API void abortWorkUnit(const char *wuid)
  10859. {
  10860. StringBuffer xpath("/WorkUnitAborts/");
  10861. xpath.append(wuid);
  10862. Owned<IRemoteConnection> acon = querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE, SDS_LOCK_TIMEOUT);
  10863. acon->queryRoot()->setPropInt(NULL, 1);
  10864. }
  10865. extern WORKUNIT_API void secSubmitWorkUnit(const char *wuid, ISecManager &secmgr, ISecUser &secuser)
  10866. {
  10867. if (checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Write, "Submit", true, true))
  10868. submitWorkUnit(wuid, secuser.getName(), secuser.credentials().getPassword());
  10869. }
  10870. extern WORKUNIT_API void secAbortWorkUnit(const char *wuid, ISecManager &secmgr, ISecUser &secuser)
  10871. {
  10872. if (!checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Write, "Submit", true, true))
  10873. return;
  10874. abortWorkUnit(wuid);
  10875. Owned<IConstWorkUnit> cw = globalFactory->openWorkUnit(wuid);
  10876. if(!cw)
  10877. return;
  10878. WorkunitUpdate wu(&cw->lock());
  10879. const char *abortBy = secuser.getName();
  10880. if (abortBy && *abortBy)
  10881. wu->setTracingValue("AbortBy", abortBy);
  10882. wu->setTracingValueInt64("AbortTimeStamp", getTimeStampNowValue());
  10883. }
  10884. extern WORKUNIT_API void submitWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  10885. {
  10886. if (secmgr && secuser)
  10887. return secSubmitWorkUnit(wuid, *secmgr, *secuser);
  10888. if (secuser)
  10889. return submitWorkUnit(wuid, secuser->getName(), secuser->credentials().getPassword());
  10890. submitWorkUnit(wuid, "", "");
  10891. }
  10892. extern WORKUNIT_API void abortWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
  10893. {
  10894. if (secmgr && secuser)
  10895. return secAbortWorkUnit(wuid, *secmgr, *secuser);
  10896. abortWorkUnit(wuid);
  10897. }
  10898. bool CLocalWorkUnit::hasWorkflow() const
  10899. {
  10900. return p->hasProp("Workflow");
  10901. }
  10902. unsigned CLocalWorkUnit::queryEventScheduledCount() const
  10903. {
  10904. CriticalBlock block(crit);
  10905. if (p->hasProp("@eventScheduledCount"))
  10906. return p->getPropInt("@eventScheduledCount", 0);
  10907. else
  10908. return p->getPropInt("Workflow/@eventScheduledCount", 0); // Legacy location for this setting
  10909. }
  10910. void CLocalWorkUnit::incEventScheduledCount()
  10911. {
  10912. CriticalBlock block(crit);
  10913. p->setPropInt("@eventScheduledCount", queryEventScheduledCount()+1);
  10914. }
  10915. IPropertyTree * CLocalWorkUnit::queryWorkflowTree() const
  10916. {
  10917. CriticalBlock block(crit);
  10918. return p->queryPropTree("Workflow");
  10919. }
  10920. IConstWorkflowItemIterator* CLocalWorkUnit::getWorkflowItems() const
  10921. {
  10922. // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
  10923. CriticalBlock block(crit);
  10924. if(!workflowIteratorCached)
  10925. {
  10926. assertex(!workflowIterator);
  10927. Owned<IPropertyTree> s = p->getPropTree("Workflow");
  10928. if(s)
  10929. workflowIterator.setown(createWorkflowItemIterator(s));
  10930. workflowIteratorCached = true;
  10931. }
  10932. return workflowIterator.getLink();
  10933. }
  10934. IWorkflowItemArray * CLocalWorkUnit::getWorkflowClone() const
  10935. {
  10936. unsigned count = 0;
  10937. Owned<IConstWorkflowItemIterator> iter = getWorkflowItems();
  10938. for(iter->first(); iter->isValid(); iter->next())
  10939. count++;
  10940. Owned<IWorkflowItemArray> array = createWorkflowItemArray(count);
  10941. for(iter->first(); iter->isValid(); iter->next())
  10942. array->addClone(iter->query());
  10943. return array.getLink();
  10944. }
  10945. IWorkflowItem * CLocalWorkUnit::addWorkflowItem(unsigned wfid, WFType type, WFMode mode, unsigned success, unsigned failure, unsigned recovery, unsigned retriesAllowed, unsigned contingencyFor)
  10946. {
  10947. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  10948. CriticalBlock block(crit);
  10949. workflowIterator.clear();
  10950. workflowIteratorCached = false;
  10951. IPropertyTree * s = p->queryPropTree("Workflow");
  10952. if(!s)
  10953. s = p->addPropTree("Workflow");
  10954. return createWorkflowItem(s, wfid, type, mode, success, failure, recovery, retriesAllowed, contingencyFor);
  10955. }
  10956. IWorkflowItemIterator * CLocalWorkUnit::updateWorkflowItems()
  10957. {
  10958. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  10959. CriticalBlock block(crit);
  10960. if(!workflowIterator)
  10961. {
  10962. IPropertyTree * s = p->queryPropTree("Workflow");
  10963. if(!s)
  10964. s = p->addPropTree("Workflow");
  10965. workflowIterator.setown(createWorkflowItemIterator(s));
  10966. workflowIteratorCached = true;
  10967. }
  10968. return workflowIterator.getLink();
  10969. }
  10970. void CLocalWorkUnit::syncRuntimeWorkflow(IWorkflowItemArray * array)
  10971. {
  10972. Owned<IWorkflowItemIterator> iter = updateWorkflowItems();
  10973. Owned<IWorkflowItem> item;
  10974. for(iter->first(); iter->isValid(); iter->next())
  10975. {
  10976. item.setown(iter->get());
  10977. item->syncRuntimeData(array->queryWfid(item->queryWfid()));
  10978. }
  10979. workflowIterator.clear();
  10980. workflowIteratorCached = false;
  10981. }
  10982. void CLocalWorkUnit::resetWorkflow()
  10983. {
  10984. if (hasWorkflow())
  10985. {
  10986. Owned<IWorkflowItemIterator> iter = updateWorkflowItems();
  10987. Owned<IWorkflowItem> wf;
  10988. for(iter->first(); iter->isValid(); iter->next())
  10989. {
  10990. wf.setown(iter->get());
  10991. wf->reset();
  10992. }
  10993. workflowIterator.clear();
  10994. workflowIteratorCached = false;
  10995. }
  10996. }
  10997. void CLocalWorkUnit::schedule()
  10998. {
  10999. CriticalBlock block(crit);
  11000. if(queryEventScheduledCount() == 0) return;
  11001. switch(getState())
  11002. {
  11003. case WUStateCompleted:
  11004. setState(WUStateWait);
  11005. break;
  11006. case WUStateFailed:
  11007. case WUStateArchived:
  11008. case WUStateAborting:
  11009. case WUStateAborted:
  11010. case WUStateScheduled:
  11011. throw MakeStringException(WUERR_CannotSchedule, "Cannot schedule workunit in this state");
  11012. }
  11013. StringBuffer rootPath;
  11014. rootPath.append("/Schedule/").append(queryClusterName());
  11015. Owned<IRemoteConnection> conn = querySDS().connect(rootPath.str(), myProcessSession(), RTM_LOCK_WRITE | RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  11016. Owned<IPropertyTree> root = conn->getRoot();
  11017. if(!root->hasChildren())
  11018. {
  11019. StringBuffer addPath;
  11020. addPath.append("/Schedulers/").append(queryClusterName());
  11021. Owned<IRemoteConnection> addConn = querySDS().connect(addPath.str(), myProcessSession(), RTM_LOCK_WRITE | RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  11022. }
  11023. char const * wuid = p->queryName();
  11024. StringBuffer xpath("*/*/");
  11025. ncnameEscape(wuid, xpath);
  11026. bool more;
  11027. do more = root->removeProp(xpath.str()); while(more);
  11028. Owned<IConstWorkflowItemIterator> iter = getWorkflowItems();
  11029. Owned<IWorkflowEvent> event;
  11030. Owned<IPropertyTree> branch1, branch2;
  11031. for(iter->first(); iter->isValid(); iter->next())
  11032. {
  11033. event.setown(iter->query()->getScheduleEvent());
  11034. if(!event) continue;
  11035. ncnameEscape(event->queryName(), xpath.clear());
  11036. ensurePTree(root, xpath.str());
  11037. branch1.setown(root->getPropTree(xpath.str()));
  11038. ncnameEscape(event->queryText(), xpath.clear());
  11039. ensurePTree(branch1, xpath.str());
  11040. branch2.setown(branch1->getPropTree(xpath.str()));
  11041. ncnameEscape(wuid, xpath.clear());
  11042. ensurePTree(branch2, xpath.str());
  11043. }
  11044. }
  11045. void CLocalWorkUnit::deschedule()
  11046. {
  11047. if(queryEventScheduledCount() == 0) return;
  11048. if(getState() == WUStateWait)
  11049. setState(WUStateCompleted);
  11050. doDescheduleWorkkunit(p->queryName());
  11051. }
  11052. EnumMapping localFileUploadTypes[] = {
  11053. { UploadTypeFileSpray, "FileSpray" },
  11054. { UploadTypeWUResult, "WUResult" },
  11055. { UploadTypeWUResultCsv, "WUResultCsv" },
  11056. { UploadTypeWUResultXml, "WUResultXml" },
  11057. { UploadTypeSize, NULL }
  11058. };
  11059. class CLocalFileUpload : public CInterface, implements IConstLocalFileUpload
  11060. {
  11061. public:
  11062. CLocalFileUpload(IPropertyTree * _tree) : tree(_tree) {}
  11063. CLocalFileUpload(unsigned id, LocalFileUploadType type, char const * source, char const * destination, char const * eventTag)
  11064. {
  11065. tree.setown(createPTree());
  11066. tree->setPropInt("@id", id);
  11067. setEnum(tree, "@type", type, localFileUploadTypes);
  11068. tree->setProp("@source", source);
  11069. tree->setProp("@destination", destination);
  11070. if (eventTag)
  11071. tree->setProp("@eventTag", eventTag);
  11072. }
  11073. IMPLEMENT_IINTERFACE;
  11074. IPropertyTree * getTree() { return tree.getLink(); }
  11075. virtual unsigned queryID() const { return tree->getPropInt("@id"); }
  11076. virtual LocalFileUploadType queryType() const { return (LocalFileUploadType)getEnum(tree, "@type", localFileUploadTypes); }
  11077. virtual IStringVal & getSource(IStringVal & ret) const { ret.set(tree->queryProp("@source")); return ret; }
  11078. virtual IStringVal & getDestination(IStringVal & ret) const { ret.set(tree->queryProp("@destination")); return ret; }
  11079. virtual IStringVal & getEventTag(IStringVal & ret) const { if(tree->hasProp("@eventTag")) ret.set(tree->queryProp("@eventTag")); else ret.clear(); return ret; }
  11080. private:
  11081. Owned<IPropertyTree> tree;
  11082. };
  11083. class CLocalFileUploadIterator : public CInterface, implements IConstLocalFileUploadIterator
  11084. {
  11085. public:
  11086. CLocalFileUploadIterator(IPropertyTree * _tree) : tree(_tree), iter(tree->getElements("LocalFileUpload")) {}
  11087. IMPLEMENT_IINTERFACE;
  11088. bool first() { return iter->first(); }
  11089. bool isValid() { return iter->isValid(); }
  11090. bool next() { return iter->next(); }
  11091. IConstLocalFileUpload * get() { return new CLocalFileUpload(&iter->get()); }
  11092. private:
  11093. Owned<IPropertyTree> tree;
  11094. Owned<IPropertyTreeIterator> iter;
  11095. };
  11096. IConstLocalFileUploadIterator * CLocalWorkUnit::getLocalFileUploads() const
  11097. {
  11098. // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
  11099. CriticalBlock block(crit);
  11100. Owned<IPropertyTree> s = p->getPropTree("LocalFileUploads");
  11101. if(s)
  11102. return new CLocalFileUploadIterator(s.getClear());
  11103. else
  11104. return NULL;
  11105. }
  11106. bool CLocalWorkUnit::requiresLocalFileUpload() const
  11107. {
  11108. SCMStringBuffer dest;
  11109. Owned<IConstWUResult> result;
  11110. Owned<IConstLocalFileUploadIterator> iter(getLocalFileUploads());
  11111. if(!iter)
  11112. return false;
  11113. for(iter->first(); iter->isValid(); iter->next())
  11114. {
  11115. Owned<IConstLocalFileUpload> upload(iter->get());
  11116. switch(upload->queryType())
  11117. {
  11118. case UploadTypeWUResult:
  11119. case UploadTypeWUResultCsv:
  11120. case UploadTypeWUResultXml:
  11121. upload->getDestination(dest);
  11122. result.setown(getResultByName(dest.str()));
  11123. if(!result)
  11124. return true;
  11125. break;
  11126. default:
  11127. throw MakeStringException(WUERR_InvalidUploadFormat, "Unsupported local file upload type %s", getEnumText(upload->queryType(), localFileUploadTypes));
  11128. }
  11129. }
  11130. return false;
  11131. }
  11132. unsigned CLocalWorkUnit::addLocalFileUpload(LocalFileUploadType type, char const * source, char const * destination, char const * eventTag)
  11133. {
  11134. // For this to be legally called, we must have the write-able interface. So we are already locked for write.
  11135. CriticalBlock block(crit);
  11136. IPropertyTree * s = p->queryPropTree("LocalFileUploads");
  11137. if(!s)
  11138. s = p->addPropTree("LocalFileUploads");
  11139. unsigned id = s->numChildren();
  11140. Owned<CLocalFileUpload> upload = new CLocalFileUpload(id, type, source, destination, eventTag);
  11141. s->addPropTree("LocalFileUpload", upload->getTree());
  11142. return id;
  11143. }
  11144. IStringVal & CLocalWorkUnit::getAbortBy(IStringVal & str) const
  11145. {
  11146. CriticalBlock block(crit);
  11147. str.set(p->queryProp("Tracing/AbortBy"));
  11148. return str;
  11149. }
  11150. unsigned __int64 CLocalWorkUnit::getAbortTimeStamp() const
  11151. {
  11152. CriticalBlock block(crit);
  11153. return p->getPropInt64("Tracing/AbortTimeStamp", 0);
  11154. }
  11155. cost_type CLocalWorkUnit::getExecuteCost() const
  11156. {
  11157. CriticalBlock block(crit);
  11158. return p->getPropInt64("@costExecute");
  11159. }
  11160. cost_type CLocalWorkUnit::getFileAccessCost() const
  11161. {
  11162. CriticalBlock block(crit);
  11163. return p->getPropInt64("@costFileAccess");
  11164. }
  11165. #if 0
  11166. void testConstWorkflow(IConstWorkflowItem * cwf, bool * okay, bool * dep)
  11167. {
  11168. DBGLOG("Test workflow const iface %u", cwf->queryWfid());
  11169. unsigned deps = 0;
  11170. Owned<IWorkflowDependencyIterator> diter;
  11171. switch(cwf->queryWfid())
  11172. {
  11173. case 1:
  11174. assertex(!cwf->isScheduled());
  11175. assertex(cwf->queryType() == WFTypeNormal);
  11176. assertex(cwf->queryState() == WFStateNull);
  11177. diter.setown(cwf->getDependencies());
  11178. for(diter->first(); diter->isValid(); diter->next())
  11179. deps++;
  11180. assertex(deps==0);
  11181. okay[0] = true;
  11182. break;
  11183. case 2:
  11184. assertex(!cwf->isScheduled());
  11185. assertex(cwf->queryType() == WFTypeRecovery);
  11186. assertex(cwf->queryState() == WFStateSkip);
  11187. okay[1] = true;
  11188. break;
  11189. case 3:
  11190. assertex(cwf->queryContingencyFor() == 4);
  11191. okay[2] = true;
  11192. break;
  11193. case 4:
  11194. assertex(cwf->isScheduled());
  11195. assertex(cwf->queryType() == WFTypeNormal);
  11196. assertex(cwf->queryState() == WFStateReqd);
  11197. assertex(cwf->querySuccess() == 0);
  11198. assertex(cwf->queryFailure() == 3);
  11199. assertex(cwf->queryRecovery() == 2);
  11200. assertex(cwf->queryRetriesAllowed() == 10);
  11201. assertex(cwf->queryRetriesRemaining() == 10);
  11202. diter.setown(cwf->getDependencies());
  11203. for(diter->first(); diter->isValid(); diter->next())
  11204. {
  11205. dep[diter->query()-1] = true;
  11206. deps++;
  11207. }
  11208. assertex(deps==2);
  11209. assertex(dep[0]);
  11210. assertex(dep[1]);
  11211. okay[3] = true;
  11212. break;
  11213. case 5:
  11214. assertex(cwf->isScheduled());
  11215. assertex(!cwf->isScheduledNow());
  11216. assertex(cwf->querySchedulePriority() == 75);
  11217. assertex(cwf->queryScheduleCount() == 5);
  11218. assertex(cwf->queryScheduleCountRemaining() == 5);
  11219. okay[4] = true;
  11220. break;
  11221. case 6:
  11222. assertex(cwf->isScheduled());
  11223. assertex(!cwf->isScheduledNow());
  11224. assertex(cwf->querySchedulePriority() == 25);
  11225. assertex(!cwf->hasScheduleCount());
  11226. okay[5] = true;
  11227. break;
  11228. default:
  11229. assertex(!"unknown wfid in test");
  11230. }
  11231. }
  11232. void testRuntimeWorkflow(IRuntimeWorkflowItem * rwf, bool * okay)
  11233. {
  11234. DBGLOG("Test workflow runtime iface %u", rwf->queryWfid());
  11235. switch(rwf->queryWfid())
  11236. {
  11237. case 1:
  11238. case 2:
  11239. case 3:
  11240. okay[rwf->queryWfid()-1] = true;
  11241. break;
  11242. case 4:
  11243. {
  11244. unsigned tries = 0;
  11245. while(rwf->testAndDecRetries())
  11246. tries++;
  11247. assertex(tries == 10);
  11248. assertex(rwf->queryRetriesRemaining() == 0);
  11249. rwf->setState(WFStateFail);
  11250. assertex(rwf->queryState() == WFStateFail);
  11251. rwf->reset();
  11252. assertex(rwf->queryRetriesRemaining() == 10);
  11253. assertex(rwf->queryState() == WFStateReqd);
  11254. }
  11255. okay[3] = true;
  11256. break;
  11257. case 5:
  11258. {
  11259. assertex(rwf->queryScheduleCountRemaining() == 5);
  11260. unsigned count = 0;
  11261. do count++; while(rwf->decAndTestScheduleCountRemaining());
  11262. assertex(count == 5);
  11263. assertex(rwf->queryScheduleCountRemaining() == 0);
  11264. rwf->reset();
  11265. assertex(rwf->queryScheduleCountRemaining() == 5);
  11266. }
  11267. okay[4] = true;
  11268. break;
  11269. case 6:
  11270. {
  11271. assertex(!rwf->hasScheduleCount());
  11272. unsigned count;
  11273. for(count=0; count<20; count++)
  11274. assertex(rwf->decAndTestScheduleCountRemaining());
  11275. }
  11276. okay[5] = true;
  11277. break;
  11278. default:
  11279. assertex(!"unknown wfid in test");
  11280. }
  11281. }
  11282. void testWorkflow()
  11283. {
  11284. DBGLOG("workunit.cpp : testWorkflow");
  11285. CLocalWorkUnit wu("W-WF-TEST", 0, 0, 0);
  11286. Owned<IWorkflowItem> wf;
  11287. wf.setown(wu.addWorkflowItem(1, WFTypeNormal, 0, 0, 0, 0, 0));
  11288. wf.setown(wu.addWorkflowItem(2, WFTypeRecovery, 0, 0, 0, 0, 0));
  11289. wf.setown(wu.addWorkflowItem(3, WFTypeFailure, 0, 0, 0, 0, 4));
  11290. wf.setown(wu.addWorkflowItem(4, WFTypeNormal, 0, 3, 2, 10, 0));
  11291. wf->setScheduledNow();
  11292. wf->addDependency(1);
  11293. wf.setown(wu.addWorkflowItem(5, WFTypeNormal, 0, 0, 0, 0, 0));
  11294. wf->setScheduledOn("test", "foo*");
  11295. wf->setSchedulePriority(75);
  11296. wf->setScheduleCount(5);
  11297. wf.setown(wu.addWorkflowItem(6, WFTypeNormal, 0, 0, 0, 0, 0));
  11298. wf->setScheduledOn("test", "bar*");
  11299. wf->setSchedulePriority(25);
  11300. unsigned const n = 6;
  11301. bool okay[n];
  11302. bool dep[n];
  11303. unsigned i;
  11304. for(i=0; i<n; i++)
  11305. okay[i] = dep[i] = 0;
  11306. Owned<IConstWorkflowItemIterator> citer(wu.getWorkflowItems());
  11307. for(citer->first(); citer->isValid(); citer->next())
  11308. testConstWorkflow(citer->query(), okay, dep);
  11309. for(i=0; i<n; i++)
  11310. {
  11311. assertex(okay[i]);
  11312. okay[i] = false;
  11313. }
  11314. Owned<IWorkflowItemIterator> miter(wu.updateWorkflowItems());
  11315. for(miter->first(); miter->isValid(); miter->next())
  11316. {
  11317. Owned<IRuntimeWorkflowItem> rwf(miter->get());
  11318. testRuntimeWorkflow(rwf, okay);
  11319. }
  11320. for(i=0; i<n; i++)
  11321. {
  11322. assertex(okay[i]);
  11323. okay[i] = dep[i] = false;
  11324. }
  11325. Owned<IWorkflowItemArray> array(wu.getWorkflowClone());
  11326. unsigned wfid;
  11327. for(wfid = 1; array->isValid(wfid); wfid++)
  11328. testConstWorkflow(&array->queryWfid(wfid), okay, dep);
  11329. for(i=0; i<n; i++)
  11330. {
  11331. assertex(okay[i]);
  11332. okay[i] = false;
  11333. }
  11334. for(wfid = 1; array->isValid(wfid); wfid++)
  11335. testRuntimeWorkflow(&array->queryWfid(wfid), okay);
  11336. for(i=0; i<n; i++)
  11337. {
  11338. assertex(okay[i]);
  11339. okay[i] = false;
  11340. }
  11341. }
  11342. #endif
  11343. //------------------------------------------------------------------------------------------
  11344. extern WUState waitForWorkUnitToComplete(const char * wuid, int timeout, std::list<WUState> expectedStates)
  11345. {
  11346. return globalFactory->waitForWorkUnit(wuid, (unsigned) timeout, false, expectedStates);
  11347. }
  11348. extern WORKUNIT_API WUState secWaitForWorkUnitToComplete(const char * wuid, ISecManager *secmgr, ISecUser *secuser, int timeout, std::list<WUState> expectedStates)
  11349. {
  11350. if (checkWuSecAccess(wuid, secmgr, secuser, SecAccess_Read, "Wait for Complete", false, true))
  11351. return waitForWorkUnitToComplete(wuid, timeout, expectedStates);
  11352. return WUStateUnknown;
  11353. }
  11354. extern bool waitForWorkUnitToCompile(const char * wuid, int timeout)
  11355. {
  11356. switch(globalFactory->waitForWorkUnit(wuid, (unsigned) timeout, true, { WUStateWait }))
  11357. {
  11358. case WUStateCompiled:
  11359. case WUStateCompleted:
  11360. case WUStateWait:
  11361. case WUStateUploadingFiles:
  11362. return true;
  11363. default:
  11364. return false;
  11365. }
  11366. }
  11367. extern WORKUNIT_API bool secWaitForWorkUnitToCompile(const char * wuid, ISecManager &secmgr, ISecUser &secuser, int timeout)
  11368. {
  11369. if (checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Read, "Wait for Compile", false, true))
  11370. return waitForWorkUnitToCompile(wuid, timeout);
  11371. return false;
  11372. }
  11373. extern WORKUNIT_API bool secDebugWorkunit(const char * wuid, ISecManager &secmgr, ISecUser &secuser, const char *command, StringBuffer &response)
  11374. {
  11375. if (strnicmp(command, "<debug:", 7) == 0 && checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Read, "Debug", false, true))
  11376. {
  11377. Owned<IConstWorkUnit> wu = globalFactory->openWorkUnit(wuid, &secmgr, &secuser);
  11378. SCMStringBuffer ip;
  11379. unsigned port = 0;
  11380. try
  11381. {
  11382. port = wu->getDebugAgentListenerPort();
  11383. wu->getDebugAgentListenerIP(ip);
  11384. SocketEndpoint debugEP(ip.str(), port);
  11385. Owned<ISocket> socket = ISocket::connect_timeout(debugEP, 1000);
  11386. unsigned len = (size32_t)strlen(command);
  11387. unsigned revlen = len;
  11388. _WINREV(revlen);
  11389. socket->write(&revlen, sizeof(revlen));
  11390. socket->write(command, len);
  11391. for (;;)
  11392. {
  11393. socket->read(&len, sizeof(len));
  11394. _WINREV(len);
  11395. if (len == 0)
  11396. break;
  11397. if (len & 0x80000000)
  11398. {
  11399. throwUnexpected();
  11400. }
  11401. char * mem = (char*) response.reserve(len);
  11402. socket->read(mem, len);
  11403. }
  11404. return true;
  11405. }
  11406. catch (IException *E)
  11407. {
  11408. VStringBuffer msg("In secDebugWorkunit wuid %s port %d ip %s command %s", wuid, port, ip.str(), command);
  11409. EXCLOG(E, msg.str());
  11410. throw;
  11411. }
  11412. }
  11413. return false;
  11414. }
  11415. void getSimpleResultType(IWUResult *r, Owned<ITypeInfo> &type)
  11416. {
  11417. TypeInfoArray types;
  11418. StringAttrArray names;
  11419. r->getSchema(types, names, NULL);
  11420. if (types.ordinality()==1)
  11421. type.set(&types.item(0));
  11422. }
  11423. bool isSuppliedParamScalar(IWUResult *r, IPropertyTree &curVal, Owned<ITypeInfo> &type)
  11424. {
  11425. if (!r->isResultScalar())
  11426. return false;
  11427. if (!curVal.hasChildren())
  11428. return true;
  11429. getSimpleResultType(r, type);
  11430. return type && type->isScalar();
  11431. }
  11432. void updateSuppliedXmlParams(IWorkUnit * w)
  11433. {
  11434. Owned<const IPropertyTree> params = w->getXmlParams();
  11435. if (!params)
  11436. return;
  11437. Owned<IPropertyTreeIterator> elems = params->getElements("*");
  11438. ForEach(*elems)
  11439. {
  11440. IPropertyTree & curVal = elems->query();
  11441. const char *name = curVal.queryName();
  11442. Owned<IWUResult> r = updateWorkUnitResult(w, name, -1);
  11443. if (r)
  11444. {
  11445. Owned<ITypeInfo> type;
  11446. StringBuffer s;
  11447. if (isSuppliedParamScalar(r, curVal, type))
  11448. {
  11449. curVal.getProp(".", s);
  11450. r->setResultXML(s);
  11451. r->setResultStatus(ResultStatusSupplied);
  11452. }
  11453. else
  11454. {
  11455. toXML(&curVal, s);
  11456. if (!type)
  11457. getSimpleResultType(r, type);
  11458. bool isSet = (type && type->getTypeCode()==type_set);
  11459. r->setResultRaw(s.length(), s.str(), isSet ? ResultFormatXmlSet : ResultFormatXml);
  11460. }
  11461. }
  11462. else
  11463. DBGLOG("WARNING: no matching variable in workunit for input parameter %s", name);
  11464. }
  11465. }
  11466. IWUResult * updateWorkUnitResult(IWorkUnit * w, const char *name, unsigned sequence)
  11467. {
  11468. switch ((int)sequence)
  11469. {
  11470. case ResultSequenceStored:
  11471. return w->updateVariableByName(name);
  11472. case ResultSequencePersist:
  11473. return w->updateGlobalByName(name);
  11474. case ResultSequenceInternal:
  11475. case ResultSequenceOnce:
  11476. return w->updateTemporaryByName(name);
  11477. default:
  11478. return w->updateResultBySequence(sequence);
  11479. }
  11480. }
  11481. IConstWUResult * getWorkUnitResult(IConstWorkUnit * w, const char *name, unsigned sequence)
  11482. {
  11483. switch ((int)sequence)
  11484. {
  11485. case ResultSequenceStored:
  11486. return w->getVariableByName(name);
  11487. case ResultSequencePersist:
  11488. return w->getGlobalByName(name);
  11489. case ResultSequenceInternal:
  11490. case ResultSequenceOnce:
  11491. return w->getTemporaryByName(name);
  11492. default:
  11493. if (name && name[0])
  11494. return w->getResultByName(name);//name takes precedence over sequence
  11495. else
  11496. return w->getResultBySequence(sequence);
  11497. }
  11498. }
  11499. extern WORKUNIT_API bool getWorkUnitCreateTime(const char *wuid,CDateTime &time)
  11500. {
  11501. if (wuid) {
  11502. char prefchar;
  11503. unsigned year,month,day,hour,min,sec;
  11504. if (sscanf(wuid, "%c%4u%2u%2u-%2u%2u%2u", &prefchar, &year, &month, &day, &hour, &min, &sec)==7) {
  11505. time.set(year, month, day, hour, min, sec, 0, true);
  11506. // time.setDate(year, month, day);
  11507. // time.setTime(hour, min, sec, 0, true); // for some reason time is local
  11508. return true;
  11509. }
  11510. }
  11511. return false;
  11512. }
  11513. extern WORKUNIT_API WUState getWorkUnitState(const char* state)
  11514. {
  11515. return (WUState) getEnum(state, states);
  11516. }
  11517. constexpr LogMsgCategory MCschedconn = MCprogress(1000); // Category used to inform about schedule synchronization
  11518. class CWorkflowScheduleConnection : implements IWorkflowScheduleConnection, public CInterface
  11519. {
  11520. public:
  11521. CWorkflowScheduleConnection(char const * wuid)
  11522. {
  11523. basexpath.append("/WorkflowSchedule/").append(wuid);
  11524. flagxpath.append(basexpath.str()).append("/Active");
  11525. }
  11526. IMPLEMENT_IINTERFACE;
  11527. virtual void lock()
  11528. {
  11529. LOG(MCschedconn, "Locking base schedule connection");
  11530. baseconn.setown(querySDS().connect(basexpath.str(), myProcessSession(), RTM_CREATE_QUERY | RTM_LOCK_WRITE, INFINITE));
  11531. if(!baseconn)
  11532. throw MakeStringException(WUERR_ScheduleLockFailed, "Could not get base workflow schedule lock");
  11533. }
  11534. virtual void unlock()
  11535. {
  11536. LOG(MCschedconn, "Unlocking base schedule connection");
  11537. baseconn.clear();
  11538. }
  11539. virtual void setActive()
  11540. {
  11541. LOG(MCschedconn, "Setting active flag in schedule connection");
  11542. flagconn.setown(querySDS().connect(flagxpath.str(), myProcessSession(), RTM_CREATE | RTM_LOCK_WRITE | RTM_DELETE_ON_DISCONNECT, INFINITE));
  11543. if(!flagconn)
  11544. throw MakeStringException(WUERR_ScheduleLockFailed, "Could not get active workflow schedule lock");
  11545. }
  11546. virtual void resetActive()
  11547. {
  11548. LOG(MCschedconn, "Resetting active flag in schedule connection");
  11549. flagconn.clear();
  11550. }
  11551. virtual bool queryActive()
  11552. {
  11553. return baseconn->queryRoot()->hasProp("Active");
  11554. }
  11555. virtual bool pull(IWorkflowItemArray * workflow)
  11556. {
  11557. assertex(baseconn);
  11558. Owned<IPropertyTree> root = baseconn->getRoot();
  11559. Owned<IPropertyTree> eventQueue = root->getPropTree("EventQueue");
  11560. if(!eventQueue) return false;
  11561. if(!eventQueue->hasProp("Item")) return false;
  11562. {
  11563. Owned<IPropertyTreeIterator> eventItems = eventQueue->getElements("Item");
  11564. Owned<IPropertyTree> eventItem;
  11565. Owned<IRuntimeWorkflowItemIterator> wfItems = workflow->getSequenceIterator();
  11566. Owned<IRuntimeWorkflowItem> wfItem;
  11567. for(eventItems->first(); eventItems->isValid(); eventItems->next())
  11568. {
  11569. eventItem.setown(&eventItems->get());
  11570. const char * eventName = eventItem->queryProp("@name");
  11571. const char * eventText = eventItem->queryProp("@text");
  11572. for(wfItems->first(); wfItems->isValid(); wfItems->next())
  11573. {
  11574. wfItem.setown(wfItems->get());
  11575. if(wfItem->queryState() != WFStateWait)
  11576. continue;
  11577. Owned<IWorkflowEvent> targetEvent = wfItem->getScheduleEvent();
  11578. if(!targetEvent || !targetEvent->matches(eventName, eventText))
  11579. continue;
  11580. wfItem->setEvent(eventName, eventText);
  11581. wfItem->setState(WFStateReqd);
  11582. resetDependentsState(workflow, *wfItem);
  11583. }
  11584. }
  11585. }
  11586. bool more;
  11587. do
  11588. more = eventQueue->removeProp("Item");
  11589. while(more);
  11590. return true;
  11591. }
  11592. virtual void push(char const * name, char const * text)
  11593. {
  11594. assertex(baseconn);
  11595. Owned<IPropertyTree> root = baseconn->getRoot();
  11596. IPropertyTree *eventQueue = ensurePTree(root, "EventQueue");
  11597. IPropertyTree *eventItem = eventQueue->addPropTree("Item");
  11598. eventItem->setProp("@name", name);
  11599. eventItem->setProp("@text", text);
  11600. }
  11601. virtual void remove()
  11602. {
  11603. if (baseconn)
  11604. {
  11605. baseconn->close(true);
  11606. baseconn.clear();
  11607. }
  11608. }
  11609. private:
  11610. void resetItemStateAndDependents(IWorkflowItemArray * workflow, unsigned wfid) const
  11611. {
  11612. if (wfid)
  11613. resetItemStateAndDependents(workflow, workflow->queryWfid(wfid));
  11614. }
  11615. void resetItemStateAndDependents(IWorkflowItemArray * workflow, IRuntimeWorkflowItem & item) const
  11616. {
  11617. switch(item.queryState())
  11618. {
  11619. case WFStateDone:
  11620. case WFStateFail:
  11621. {
  11622. item.setState(WFStateNull);
  11623. resetItemStateAndDependents(workflow, item.queryPersistWfid());
  11624. resetDependentsState(workflow, item);
  11625. break;
  11626. }
  11627. }
  11628. }
  11629. void resetDependentsState(IWorkflowItemArray * workflow, IRuntimeWorkflowItem & item) const
  11630. {
  11631. Owned<IWorkflowDependencyIterator> iter(item.getDependencies());
  11632. for(iter->first(); iter->isValid(); iter->next())
  11633. {
  11634. IRuntimeWorkflowItem & dep = workflow->queryWfid(iter->query());
  11635. resetItemStateAndDependents(workflow, dep);
  11636. }
  11637. }
  11638. private:
  11639. StringBuffer basexpath;
  11640. StringBuffer flagxpath;
  11641. Owned<IRemoteConnection> baseconn;
  11642. Owned<IRemoteConnection> flagconn;
  11643. };
  11644. extern WORKUNIT_API IWorkflowScheduleConnection * getWorkflowScheduleConnection(char const * wuid)
  11645. {
  11646. return new CWorkflowScheduleConnection(wuid);
  11647. }
  11648. extern WORKUNIT_API IExtendedWUInterface * queryExtendedWU(IConstWorkUnit * wu)
  11649. {
  11650. return QUERYINTERFACE(wu, IExtendedWUInterface);
  11651. }
  11652. extern WORKUNIT_API const IExtendedWUInterface * queryExtendedWU(const IConstWorkUnit * wu)
  11653. {
  11654. return QUERYINTERFACE(wu, const IExtendedWUInterface);
  11655. }
  11656. extern WORKUNIT_API void addExceptionToWorkunit(IWorkUnit * wu, ErrorSeverity severity, const char * source, unsigned code, const char * text, const char * filename, unsigned lineno, unsigned column, unsigned activity)
  11657. {
  11658. Owned<IWUException> we = wu->createException();
  11659. we->setSeverity(severity);
  11660. we->setExceptionMessage(text);
  11661. if (source)
  11662. we->setExceptionSource(source);
  11663. if (code)
  11664. we->setExceptionCode(code);
  11665. if (filename)
  11666. we->setExceptionFileName(filename);
  11667. if (lineno)
  11668. {
  11669. we->setExceptionLineNo(lineno);
  11670. if (column)
  11671. we->setExceptionColumn(lineno);
  11672. }
  11673. if (activity)
  11674. we->setActivityId(activity);
  11675. }
  11676. const char * skipLeadingXml(const char * text)
  11677. {
  11678. if (!text)
  11679. return NULL;
  11680. //skip utf8 BOM, probably excessive
  11681. if (memcmp(text, UTF8_BOM, 3) == 0)
  11682. text += 3;
  11683. for (;;)
  11684. {
  11685. if (isspace(*text))
  11686. text++;
  11687. else if (text[0] == '<' && text[1] == '?')
  11688. {
  11689. text += 2;
  11690. for (;;)
  11691. {
  11692. if (!*text) break;
  11693. if (text[0] == '?' && text[1] == '>')
  11694. {
  11695. text += 2;
  11696. break;
  11697. }
  11698. text++;
  11699. }
  11700. }
  11701. else if (text[0] == '<' && text[1] == '!' && text[2] == '-' && text[3] == '-')
  11702. {
  11703. text += 4;
  11704. for (;;)
  11705. {
  11706. if (!*text) break;
  11707. if (text[0] == '-' && text[1] == '-' && text[2] == '>')
  11708. {
  11709. text += 3;
  11710. break;
  11711. }
  11712. text++;
  11713. }
  11714. }
  11715. else
  11716. break;
  11717. }
  11718. return text;
  11719. }
  11720. extern WORKUNIT_API bool isArchiveQuery(const char * text)
  11721. {
  11722. text = skipLeadingXml(text);
  11723. if (!text)
  11724. return false;
  11725. const char * archivePrefix = "<Archive";
  11726. return memicmp(text, archivePrefix, strlen(archivePrefix)) == 0;
  11727. }
  11728. extern WORKUNIT_API bool isQueryManifest(const char * text)
  11729. {
  11730. text = skipLeadingXml(text);
  11731. if (!text)
  11732. return false;
  11733. const char * manifestPrefix = "<Manifest";
  11734. return memicmp(text, manifestPrefix, strlen(manifestPrefix)) == 0;
  11735. }
  11736. //------------------------------------------------------------------------------
  11737. // Named Alias helper function
  11738. static IPropertyTree * resolveQueryByDll(IPropertyTree * queryRegistry, const char * dll)
  11739. {
  11740. StringBuffer xpath;
  11741. xpath.append("Query[@dll=\"").append(dll).append("\"]");
  11742. return queryRegistry->getPropTree(xpath);
  11743. }
  11744. static IPropertyTree * resolveQueryByWuid(IPropertyTree * queryRegistry, const char * wuid)
  11745. {
  11746. StringBuffer xpath;
  11747. xpath.append("Query[@wuid=\"").append(wuid).append("\"]");
  11748. return queryRegistry->getPropTree(xpath);
  11749. }
  11750. static void clearAliases(IPropertyTree * queryRegistry, const char * id)
  11751. {
  11752. StringBuffer lcId(id);
  11753. lcId.toLowerCase();
  11754. StringBuffer xpath;
  11755. xpath.append("Alias[@id=\"").append(lcId).append("\"]");
  11756. while (queryRegistry->removeProp(xpath));
  11757. }
  11758. IPropertyTree * addNamedQuery(IPropertyTree * queryRegistry, const char * name, const char * wuid, const char * dll, bool library, const char *userid, const char *snapshot)
  11759. {
  11760. StringBuffer lcName(name);
  11761. lcName.toLowerCase();
  11762. StringBuffer xpath;
  11763. xpath.append("Query[@name=\"").append(lcName.str()).append("\"]");
  11764. Owned<IPropertyTreeIterator> iter = queryRegistry->getElements(xpath);
  11765. unsigned seq = 1;
  11766. ForEach(*iter)
  11767. {
  11768. IPropertyTree &item = iter->query();
  11769. const char *thisWuid = item.queryProp("@wuid");
  11770. if (strieq(wuid, thisWuid))
  11771. return &item;
  11772. unsigned thisSeq = item.getPropInt("@seq");
  11773. if (thisSeq >= seq)
  11774. seq = thisSeq + 1;
  11775. }
  11776. StringBuffer id;
  11777. id.append(lcName).append(".").append(seq);
  11778. IPropertyTree * newEntry = createPTree("Query", ipt_caseInsensitive|ipt_lowmem);
  11779. newEntry->setProp("@name", lcName);
  11780. newEntry->setProp("@wuid", wuid);
  11781. newEntry->setProp("@dll", dll);
  11782. newEntry->setProp("@id", id);
  11783. newEntry->setPropInt("@seq", seq);
  11784. if (library)
  11785. newEntry->setPropBool("@isLibrary", true);
  11786. if (userid && *userid)
  11787. newEntry->setProp("@publishedBy", userid);
  11788. if (snapshot && *snapshot)
  11789. newEntry->setProp("@snapshot", snapshot);
  11790. return queryRegistry->addPropTree("Query", newEntry);
  11791. }
  11792. void removeNamedQuery(IPropertyTree * queryRegistry, const char * id)
  11793. {
  11794. StringBuffer lcId(id);
  11795. lcId.toLowerCase();
  11796. clearAliases(queryRegistry, lcId);
  11797. StringBuffer xpath;
  11798. xpath.append("Query[@id=\"").append(lcId).append("\"]");
  11799. queryRegistry->removeProp(xpath);
  11800. }
  11801. void removeDllFromNamedQueries(IPropertyTree * queryRegistry, const char * dll)
  11802. {
  11803. Owned<IPropertyTree> match = resolveQueryByDll(queryRegistry, dll);
  11804. if (!match)
  11805. return;
  11806. clearAliases(queryRegistry, match->queryProp("@id"));
  11807. queryRegistry->removeTree(match);
  11808. }
  11809. void removeWuidFromNamedQueries(IPropertyTree * queryRegistry, const char * wuid)
  11810. {
  11811. Owned<IPropertyTree> match = resolveQueryByWuid(queryRegistry, wuid);
  11812. if (!match)
  11813. return;
  11814. clearAliases(queryRegistry, match->queryProp("@id"));
  11815. queryRegistry->removeTree(match);
  11816. }
  11817. void removeAliasesFromNamedQuery(IPropertyTree * queryRegistry, const char * id)
  11818. {
  11819. clearAliases(queryRegistry, id);
  11820. }
  11821. void setQueryAlias(IPropertyTree * queryRegistry, const char * name, const char * value)
  11822. {
  11823. StringBuffer lcName(name);
  11824. lcName.toLowerCase();
  11825. StringBuffer xpath;
  11826. xpath.append("Alias[@name=\"").append(lcName).append("\"]");
  11827. IPropertyTree * match = queryRegistry->queryPropTree(xpath);
  11828. if (!match)
  11829. {
  11830. match = queryRegistry->addPropTree("Alias");
  11831. match->setProp("@name", lcName);
  11832. }
  11833. match->setProp("@id", value);
  11834. }
  11835. extern WORKUNIT_API IPropertyTree * getQueryById(IPropertyTree * queryRegistry, const char *queryid)
  11836. {
  11837. if (!queryRegistry || !queryid)
  11838. return NULL;
  11839. StringBuffer xpath;
  11840. xpath.append("Query[@id=\"").append(queryid).append("\"]");
  11841. return queryRegistry->getPropTree(xpath);
  11842. }
  11843. extern WORKUNIT_API IPropertyTree * getQueryById(const char *queryset, const char *queryid, bool readonly)
  11844. {
  11845. Owned<IPropertyTree> queryRegistry = getQueryRegistry(queryset, readonly);
  11846. return getQueryById(queryRegistry, queryid);
  11847. }
  11848. extern WORKUNIT_API IPropertyTree * resolveQueryAlias(IPropertyTree * queryRegistry, const char * alias)
  11849. {
  11850. if (!queryRegistry || !alias)
  11851. return NULL;
  11852. StringBuffer xpath;
  11853. unsigned cnt = 0;
  11854. StringBuffer lc(alias);
  11855. const char * search = lc.toLowerCase().str();
  11856. for (;;)
  11857. {
  11858. xpath.set("Alias[@name='").append(search).append("']/@id");
  11859. const char * queryId = queryRegistry->queryProp(xpath);
  11860. if (!queryId)
  11861. break;
  11862. //Check for too many alias indirections.
  11863. if (cnt++ > 10)
  11864. return NULL;
  11865. search = lc.set(queryId).toLowerCase().str();
  11866. }
  11867. return getQueryById(queryRegistry, search);
  11868. }
  11869. extern WORKUNIT_API IPropertyTree * resolveQueryAlias(const char *queryset, const char *alias, bool readonly)
  11870. {
  11871. Owned<IPropertyTree> queryRegistry = getQueryRegistry(queryset, readonly);
  11872. return resolveQueryAlias(queryRegistry, alias);
  11873. }
  11874. void setQuerySuspendedState(IPropertyTree * queryRegistry, const char *id, bool suspend, const char *userid)
  11875. {
  11876. StringBuffer lcId(id);
  11877. lcId.toLowerCase();
  11878. StringBuffer xpath;
  11879. xpath.append("Query[@id=\"").append(lcId).append("\"]");
  11880. IPropertyTree *tree = queryRegistry->queryPropTree(xpath);
  11881. if (tree)
  11882. {
  11883. if (tree->getPropBool("@suspended", false) == suspend)
  11884. return;
  11885. if (suspend)
  11886. {
  11887. tree->addPropBool("@suspended", true);
  11888. if (userid && *userid)
  11889. tree->addProp("@suspendedBy", userid);
  11890. }
  11891. else
  11892. {
  11893. tree->removeProp("@suspended");
  11894. tree->removeProp("@suspendedBy");
  11895. }
  11896. }
  11897. else
  11898. throw MakeStringException((suspend)? QUERRREG_SUSPEND : QUERRREG_UNSUSPEND, "Modifying query suspended state failed. Could not find query %s", id);
  11899. }
  11900. void setQueryCommentForNamedQuery(IPropertyTree * queryRegistry, const char *id, const char *queryComment)
  11901. {
  11902. if (queryComment)
  11903. {
  11904. StringBuffer lcId(id);
  11905. lcId.toLowerCase();
  11906. StringBuffer xpath;
  11907. xpath.append("Query[@id=\"").append(lcId).append("\"]");
  11908. IPropertyTree *tree = queryRegistry->queryPropTree(xpath);
  11909. if (tree)
  11910. tree->setProp("@queryComment", queryComment);
  11911. else
  11912. throw MakeStringException(QUERRREG_COMMENT, "Could not find query %s", id);
  11913. }
  11914. }
  11915. extern WORKUNIT_API IPropertyTree * getQueryRegistryRoot()
  11916. {
  11917. Owned<IRemoteConnection> conn = querySDS().connect("/QuerySets", myProcessSession(), RTM_LOCK_READ, SDS_LOCK_TIMEOUT);
  11918. if (conn)
  11919. return conn->getRoot();
  11920. else
  11921. return NULL;
  11922. }
  11923. extern WORKUNIT_API void checkAddLibrariesToQueryEntry(IPropertyTree *queryTree, IConstWULibraryIterator *libraries)
  11924. {
  11925. if (!queryTree || !libraries)
  11926. return;
  11927. if (queryTree->hasProp("@libCount")) //already added
  11928. return;
  11929. unsigned libCount=0;
  11930. ForEach(*libraries)
  11931. {
  11932. IConstWULibrary &library = libraries->query();
  11933. SCMStringBuffer libname;
  11934. if (!library.getName(libname).length())
  11935. continue;
  11936. queryTree->addProp("Library", libname.str());
  11937. libCount++;
  11938. }
  11939. queryTree->setPropInt("@libCount", libCount);
  11940. }
  11941. extern WORKUNIT_API void checkAddLibrariesToQueryEntry(IPropertyTree *queryTree, IConstWorkUnit *cw)
  11942. {
  11943. Owned<IConstWULibraryIterator> libraries = &cw->getLibraries();
  11944. checkAddLibrariesToQueryEntry(queryTree, libraries);
  11945. }
  11946. extern WORKUNIT_API IPropertyTree * getQueryRegistry(const char * wsEclId, bool readonly)
  11947. {
  11948. //Only lock the branch for the target we're interested in.
  11949. StringBuffer xpath;
  11950. xpath.append("/QuerySets/QuerySet[@id=\"").append(wsEclId).append("\"]");
  11951. Owned<IRemoteConnection> conn = querySDS().connect(xpath.str(), myProcessSession(), readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  11952. if (conn)
  11953. return conn->getRoot();
  11954. if (readonly)
  11955. return NULL;
  11956. //Lock the QuerySets in case another thread/client wants to check/add the same QuerySet.
  11957. Owned<IRemoteConnection> globalLock = querySDS().connect("/QuerySets/", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  11958. //Re-check if the QuerySet has been added between checking the 1st time and gaining the globalLock.
  11959. conn.setown(querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
  11960. if (conn)
  11961. return conn->getRoot();
  11962. conn.setown(querySDS().connect("/QuerySets/QuerySet", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_ADD, SDS_LOCK_TIMEOUT));
  11963. if (!conn)
  11964. throwUnexpected();
  11965. IPropertyTree * root = conn->queryRoot();
  11966. root->setProp("@id",wsEclId);
  11967. conn->commit();
  11968. return LINK(root);
  11969. }
  11970. IPropertyTree * addNamedPackageSet(IPropertyTree * packageRegistry, const char * name, IPropertyTree *packageInfo, bool overWrite)
  11971. {
  11972. StringBuffer xpath;
  11973. StringBuffer lcName(name);
  11974. lcName.toLowerCase();
  11975. // see if "name" already exists
  11976. xpath.append("Package[@id='").append(name).append("']");
  11977. IPropertyTree *pkgTree = packageRegistry->queryPropTree(xpath.str());
  11978. if (pkgTree)
  11979. {
  11980. if (overWrite)
  11981. packageRegistry->removeTree(pkgTree);
  11982. else
  11983. throw MakeStringException(WUERR_PackageAlreadyExists, "Package name %s already exists, either delete it or specify overwrite",name);
  11984. }
  11985. IPropertyTree *tree = packageRegistry->addPropTree("Package", packageInfo);
  11986. tree->setProp("@id", lcName);
  11987. return tree;
  11988. }
  11989. void removeNamedPackage(IPropertyTree * packageRegistry, const char * id)
  11990. {
  11991. StringBuffer lcId(id);
  11992. lcId.toLowerCase();
  11993. StringBuffer xpath;
  11994. xpath.append("Package[@id=\"").append(lcId).append("\"]");
  11995. packageRegistry->removeProp(xpath);
  11996. }
  11997. extern WORKUNIT_API IPropertyTree * getPackageSetRegistry(const char * wsEclId, bool readonly)
  11998. {
  11999. //Only lock the branch for the target we're interested in.
  12000. StringBuffer xpath;
  12001. xpath.append("/PackageSets/PackageSet[@id=\"").append(wsEclId).append("\"]");
  12002. Owned<IRemoteConnection> conn = querySDS().connect(xpath.str(), myProcessSession(), readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  12003. if (conn)
  12004. return conn->getRoot();
  12005. if (readonly)
  12006. return NULL;
  12007. //Lock the PackageSets in case another thread/client wants to check/add the same PackageSet.
  12008. Owned<IRemoteConnection> globalLock = querySDS().connect("/PackageSets/", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
  12009. //Re-check if the PackageSet has been added between checking the 1st time and gaining the globalLock.
  12010. conn.setown(querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
  12011. if (conn)
  12012. return conn->getRoot();
  12013. conn.setown(querySDS().connect("/PackageSets/PackageSet", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_ADD, SDS_LOCK_TIMEOUT));
  12014. if (!conn)
  12015. throwUnexpected();
  12016. IPropertyTree* root = conn->queryRoot();
  12017. root->setProp("@id",wsEclId);
  12018. conn->commit();
  12019. return LINK(root);
  12020. }
  12021. void addQueryToQuerySet(IWorkUnit *workunit, IPropertyTree *queryRegistry, const char *queryName, WUQueryActivationOptions activateOption, StringBuffer &newQueryId, const char *userid)
  12022. {
  12023. StringBuffer cleanQueryName;
  12024. appendUtf8XmlName(cleanQueryName, strlen(queryName), queryName);
  12025. SCMStringBuffer dllName;
  12026. Owned<IConstWUQuery> q = workunit->getQuery();
  12027. q->getQueryDllName(dllName);
  12028. if (!dllName.length())
  12029. throw MakeStringException(WUERR_InvalidDll, "Cannot deploy query - no associated dll.");
  12030. StringBuffer currentTargetClusterType;
  12031. queryRegistry->getProp("@targetclustertype", currentTargetClusterType);
  12032. SCMStringBuffer targetClusterType;
  12033. workunit->getDebugValue("targetclustertype", targetClusterType);
  12034. SCMStringBuffer snapshot;
  12035. workunit->getSnapshot(snapshot);
  12036. if (currentTargetClusterType.length() < 1)
  12037. {
  12038. queryRegistry->setProp("@targetclustertype", targetClusterType.str());
  12039. }
  12040. else
  12041. {
  12042. if (strcmp(currentTargetClusterType.str(), "roxie") == 0 && strcmp(currentTargetClusterType.str(), targetClusterType.str())!=0)
  12043. {
  12044. throw MakeStringException(WUERR_MismatchClusterType, "TargetClusterTypes of workunit and queryset do not match.");
  12045. }
  12046. }
  12047. IPropertyTree *newEntry = addNamedQuery(queryRegistry, cleanQueryName, workunit->queryWuid(), dllName.str(), isLibrary(workunit), userid, snapshot.str());
  12048. Owned<IConstWULibraryIterator> libraries = &workunit->getLibraries();
  12049. checkAddLibrariesToQueryEntry(newEntry, libraries);
  12050. newQueryId.append(newEntry->queryProp("@id"));
  12051. workunit->setIsQueryService(true); //will check querysets before delete
  12052. workunit->commit();
  12053. activateQuery(queryRegistry, activateOption, queryName, newQueryId, userid);
  12054. }
  12055. void activateQuery(IPropertyTree *queryRegistry, WUQueryActivationOptions activateOption, const char *queryName, const char *queryId, const char *userid)
  12056. {
  12057. StringBuffer cleanQueryName;
  12058. appendUtf8XmlName(cleanQueryName, strlen(queryName), queryName);
  12059. if (activateOption == ACTIVATE_SUSPEND_PREVIOUS|| activateOption == ACTIVATE_DELETE_PREVIOUS)
  12060. {
  12061. Owned<IPropertyTree> prevQuery = resolveQueryAlias(queryRegistry, cleanQueryName);
  12062. setQueryAlias(queryRegistry, cleanQueryName, queryId);
  12063. if (prevQuery && !streq(queryId, prevQuery->queryProp("@id")))
  12064. {
  12065. if (activateOption == ACTIVATE_SUSPEND_PREVIOUS)
  12066. setQuerySuspendedState(queryRegistry, prevQuery->queryProp("@id"), true, userid);
  12067. else
  12068. removeNamedQuery(queryRegistry, prevQuery->queryProp("@id"));
  12069. }
  12070. }
  12071. else if (activateOption == MAKE_ACTIVATE || activateOption == MAKE_ACTIVATE_LOAD_DATA_ONLY)
  12072. setQueryAlias(queryRegistry, cleanQueryName, queryId);
  12073. }
  12074. void addQueryToQuerySet(IWorkUnit *workunit, const char *querySetName, const char *queryName, WUQueryActivationOptions activateOption, StringBuffer &newQueryId, const char *userid)
  12075. {
  12076. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, false);
  12077. addQueryToQuerySet(workunit, queryRegistry, queryName, activateOption, newQueryId, userid);
  12078. }
  12079. bool removeQuerySetAlias(const char *querySetName, const char *alias)
  12080. {
  12081. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
  12082. StringBuffer xpath;
  12083. xpath.appendf("Alias[@name='%s']", alias);
  12084. IPropertyTree *t = queryRegistry->queryPropTree(xpath);
  12085. return queryRegistry->removeTree(t);
  12086. }
  12087. void addQuerySetAlias(const char *querySetName, const char *alias, const char *id)
  12088. {
  12089. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, false);
  12090. setQueryAlias(queryRegistry, alias, id);
  12091. }
  12092. void setSuspendQuerySetQuery(const char *querySetName, const char *id, bool suspend, const char *userid)
  12093. {
  12094. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
  12095. setQuerySuspendedState(queryRegistry, id, suspend, userid);
  12096. }
  12097. void deleteQuerySetQuery(const char *querySetName, const char *id)
  12098. {
  12099. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
  12100. removeNamedQuery(queryRegistry, id);
  12101. }
  12102. void removeQuerySetAliasesFromNamedQuery(const char *querySetName, const char * id)
  12103. {
  12104. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
  12105. clearAliases(queryRegistry, id);
  12106. }
  12107. void setQueryCommentForNamedQuery(const char *querySetName, const char *id, const char *queryComment)
  12108. {
  12109. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
  12110. setQueryCommentForNamedQuery(queryRegistry, id, queryComment);
  12111. }
  12112. const char *queryIdFromQuerySetWuid(IPropertyTree *queryRegistry, const char *wuid, const char *queryName, IStringVal &id)
  12113. {
  12114. if (!queryRegistry)
  12115. return NULL;
  12116. StringBuffer xpath;
  12117. xpath.appendf("Query[@wuid='%s']", wuid);
  12118. if (queryName && *queryName)
  12119. xpath.appendf("[@name='%s']", queryName);
  12120. IPropertyTree *q = queryRegistry->queryPropTree(xpath.str());
  12121. if (q)
  12122. {
  12123. id.set(q->queryProp("@id"));
  12124. }
  12125. return id.str();
  12126. }
  12127. const char *queryIdFromQuerySetWuid(const char *querySetName, const char *wuid, const char *queryName, IStringVal &id)
  12128. {
  12129. Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
  12130. return queryIdFromQuerySetWuid(queryRegistry, wuid, queryName, id);
  12131. }
  12132. extern WORKUNIT_API void gatherLibraryNames(StringArray &names, StringArray &unresolved, IWorkUnitFactory &workunitFactory, IConstWorkUnit &cw, IPropertyTree *queryset)
  12133. {
  12134. Owned<IConstWULibraryIterator> wulibraries = &cw.getLibraries();
  12135. ForEach(*wulibraries)
  12136. {
  12137. SCMStringBuffer libname;
  12138. IConstWULibrary &wulibrary = wulibraries->query();
  12139. wulibrary.getName(libname);
  12140. if (names.contains(libname.str()) || unresolved.contains(libname.str()))
  12141. continue;
  12142. Owned<IPropertyTree> query = resolveQueryAlias(queryset, libname.str());
  12143. if (query && query->getPropBool("@isLibrary"))
  12144. {
  12145. const char *wuid = query->queryProp("@wuid");
  12146. Owned<IConstWorkUnit> libcw = workunitFactory.openWorkUnit(wuid);
  12147. if (libcw)
  12148. {
  12149. names.appendUniq(libname.str());
  12150. gatherLibraryNames(names, unresolved, workunitFactory, *libcw, queryset);
  12151. continue;
  12152. }
  12153. }
  12154. unresolved.appendUniq(libname.str());
  12155. }
  12156. }
  12157. bool looksLikeAWuid(const char * wuid, const char firstChar)
  12158. {
  12159. if (!wuid)
  12160. return false;
  12161. if (wuid[0] != firstChar)
  12162. return false;
  12163. if (!isdigit(wuid[1]) || !isdigit(wuid[2]) || !isdigit(wuid[3]) || !isdigit(wuid[4]))
  12164. return false;
  12165. if (!isdigit(wuid[5]) || !isdigit(wuid[6]) || !isdigit(wuid[7]) || !isdigit(wuid[8]))
  12166. return false;
  12167. return (wuid[9]=='-');
  12168. }
  12169. IPropertyTree * resolveDefinitionInArchive(IPropertyTree * archive, const char * path)
  12170. {
  12171. IPropertyTree * module = archive;
  12172. const char * dot = strrchr(path, '.');
  12173. StringBuffer xpath;
  12174. if (dot)
  12175. {
  12176. xpath.clear().append("Module[@key='").appendLower(dot-path, path).append("']");
  12177. module = archive->queryPropTree(xpath);
  12178. path = dot+1;
  12179. }
  12180. else
  12181. module = archive->queryPropTree("Module[@key='']");
  12182. if (!module)
  12183. return NULL;
  12184. xpath.clear().append("Attribute[@key='").appendLower(strlen(path), path).append("']");
  12185. return module->queryPropTree(xpath);
  12186. }
  12187. extern WORKUNIT_API void associateLocalFile(IWUQuery * query, WUFileType type, const char * name, const char * description, unsigned crc, unsigned minActivity, unsigned maxActivity)
  12188. {
  12189. StringBuffer fullPathName;
  12190. makeAbsolutePath(name, fullPathName);
  12191. if (isContainerized())
  12192. {
  12193. StringBuffer dllDirectory;
  12194. if (getConfigurationDirectory(nullptr, "query", nullptr, nullptr, dllDirectory))
  12195. {
  12196. StringBuffer destPathName(dllDirectory);
  12197. addNonEmptyPathSepChar(destPathName);
  12198. splitFilename(fullPathName.str(), nullptr, nullptr, &destPathName, &destPathName);
  12199. OwnedIFile source = createIFile(fullPathName);
  12200. OwnedIFile target = createIFile(destPathName);
  12201. if (!target->exists())
  12202. {
  12203. source->copyTo(target, 0, NULL, true);
  12204. }
  12205. query->addAssociatedFile(type, destPathName, "localhost", description, crc, minActivity, maxActivity);
  12206. // Should we delete the local files? No - they may not be finished with
  12207. }
  12208. else
  12209. {
  12210. //Containerized stand alone eclcc or other examples with no configuration file
  12211. query->addAssociatedFile(type, fullPathName, "localhost", description, crc, minActivity, maxActivity);
  12212. }
  12213. }
  12214. else
  12215. {
  12216. StringBuffer hostname;
  12217. queryHostIP().getIpText(hostname);
  12218. query->addAssociatedFile(type, fullPathName, hostname, description, crc, minActivity, maxActivity);
  12219. }
  12220. }
  12221. extern WORKUNIT_API void descheduleWorkunit(char const * wuid)
  12222. {
  12223. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  12224. Owned<IWorkUnit> workunit = factory->updateWorkUnit(wuid);
  12225. if(workunit)
  12226. workunit->deschedule();
  12227. else
  12228. doDescheduleWorkkunit(wuid);
  12229. }
  12230. extern WORKUNIT_API void updateWorkunitStat(IWorkUnit * wu, StatisticScopeType scopeType, const char * scope, StatisticKind kind, const char * description, unsigned __int64 value, unsigned wfid)
  12231. {
  12232. StringBuffer scopestr;
  12233. if (wfid && scope && *scope)
  12234. scopestr.append(WorkflowScopePrefix).append(wfid).append(":").append(scope);
  12235. else
  12236. scopestr.set(scope);
  12237. wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scopestr, kind, description, value, 1, 0, StatsMergeReplace);
  12238. }
  12239. class WuTimingUpdater : implements ITimeReportInfo
  12240. {
  12241. public:
  12242. WuTimingUpdater(IWorkUnit * _wu, StatisticScopeType _scopeType, StatisticKind _kind)
  12243. : wu(_wu), scopeType(_scopeType), kind(_kind)
  12244. { }
  12245. virtual void report(const char * scope, const __int64 totaltime, const __int64 maxtime, const unsigned count)
  12246. {
  12247. wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, nullptr, totaltime, count, maxtime, StatsMergeReplace);
  12248. }
  12249. protected:
  12250. IWorkUnit * wu;
  12251. StatisticScopeType scopeType;
  12252. StatisticKind kind;
  12253. };
  12254. extern WORKUNIT_API void updateWorkunitTimings(IWorkUnit * wu, ITimeReporter *timer)
  12255. {
  12256. WuTimingUpdater target(wu, SSTsection, StTimeTotalExecute);
  12257. timer->report(target);
  12258. }
  12259. extern WORKUNIT_API void updateWorkunitTimings(IWorkUnit * wu, StatisticScopeType scopeType, StatisticKind kind, ITimeReporter *timer)
  12260. {
  12261. WuTimingUpdater target(wu, scopeType, kind);
  12262. timer->report(target);
  12263. }
  12264. extern WORKUNIT_API void addTimeStamp(IWorkUnit * wu, StatisticScopeType scopeType, const char * scope, StatisticKind kind, unsigned wfid)
  12265. {
  12266. StringBuffer scopestr;
  12267. if (wfid && scope && *scope)
  12268. scopestr.append(WorkflowScopePrefix).append(wfid).append(":").append(scope);
  12269. else
  12270. scopestr.set(scope);
  12271. wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scopestr, kind, NULL, getTimeStampNowValue(), 1, 0, StatsMergeAppend);
  12272. }
  12273. static double getCpuSize(const char *resourceName)
  12274. {
  12275. Owned<IPropertyTree> compConfig = getComponentConfig();
  12276. const char * cpuRequestedStr = compConfig->queryProp(resourceName);
  12277. if (!cpuRequestedStr)
  12278. return 0.0;
  12279. char * endptr;
  12280. double cpuRequested = strtod(cpuRequestedStr, &endptr);
  12281. if (cpuRequested < 0.0)
  12282. return 0.0;
  12283. if (*endptr == 'm')
  12284. cpuRequested /= 1000;
  12285. return cpuRequested;
  12286. }
  12287. static double getCostCpuHour()
  12288. {
  12289. double costCpuHour = 0.0;
  12290. if (getComponentConfigSP()->hasProp("cost/@perCpu"))
  12291. costCpuHour = getComponentConfigSP()->getPropReal("cost/@perCpu");
  12292. else
  12293. costCpuHour = getGlobalConfigSP()->getPropReal("cost/@perCpu");
  12294. if (costCpuHour < 0.0)
  12295. return 0.0;
  12296. return costCpuHour;
  12297. }
  12298. extern WORKUNIT_API double getMachineCostRate()
  12299. {
  12300. unsigned numCpus = isContainerized() ? getCpuSize("resources/@cpu") : getAffinityCpus();
  12301. return getCostCpuHour() * numCpus;
  12302. }
  12303. extern WORKUNIT_API double getThorManagerRate()
  12304. {
  12305. unsigned numCpus = isContainerized() ? getCpuSize("managerResources/@cpu") : getAffinityCpus();
  12306. return getCostCpuHour() * numCpus ;
  12307. }
  12308. extern WORKUNIT_API double getThorWorkerRate()
  12309. {
  12310. unsigned numCpus = isContainerized() ? getCpuSize("workerResources/@cpu") : getAffinityCpus();
  12311. return getCostCpuHour() * numCpus ;
  12312. }
  12313. extern WORKUNIT_API double calculateThorCost(unsigned __int64 ms, unsigned clusterWidth)
  12314. {
  12315. return calcCost(getThorManagerRate(), ms) + calcCost(getThorWorkerRate(), ms) * clusterWidth;
  12316. }
  12317. void aggregateStatistic(StatsAggregation & result, IConstWorkUnit * wu, const WuScopeFilter & filter, StatisticKind search)
  12318. {
  12319. SimpleReferenceAggregator aggregator(search, result);
  12320. Owned<IConstWUScopeIterator> it = &wu->getScopeIterator(filter);
  12321. ForEach(*it)
  12322. it->playProperties(aggregator);
  12323. }
  12324. class GlobalStatisticGatherer : public CInterfaceOf<IStatisticGatherer>
  12325. {
  12326. public:
  12327. GlobalStatisticGatherer(IWorkUnit * _wu) : wu(_wu) {}
  12328. virtual void beginScope(const StatsScopeId & id)
  12329. {
  12330. prevLenStack.append(scope.length());
  12331. if (scope.length())
  12332. scope.append(":");
  12333. id.getScopeText(scope);
  12334. scopeTypeStack.append(id.queryScopeType());
  12335. }
  12336. virtual void beginSubGraphScope(unsigned id)
  12337. {
  12338. StatsScopeId scopeId(SSTsubgraph, id);
  12339. beginScope(scopeId);
  12340. }
  12341. virtual void beginActivityScope(unsigned id)
  12342. {
  12343. StatsScopeId scopeId(SSTactivity, id);
  12344. beginScope(scopeId);
  12345. }
  12346. virtual void beginEdgeScope(unsigned id, unsigned oid)
  12347. {
  12348. StatsScopeId scopeId(SSTedge, id, oid);
  12349. beginScope(scopeId);
  12350. }
  12351. virtual void beginChildGraphScope(unsigned id)
  12352. {
  12353. StatsScopeId scopeId(SSTchildgraph, id);
  12354. beginScope(scopeId);
  12355. }
  12356. virtual void beginChannelScope(unsigned id)
  12357. {
  12358. StatsScopeId scopeId(SSTchannel, id);
  12359. beginScope(scopeId);
  12360. }
  12361. virtual void endScope()
  12362. {
  12363. scope.setLength(prevLenStack.popGet());
  12364. scopeTypeStack.pop();
  12365. }
  12366. virtual void addStatistic(StatisticKind kind, unsigned __int64 value)
  12367. {
  12368. StatisticScopeType scopeType = scopeTypeStack.ordinality() ? (StatisticScopeType)scopeTypeStack.tos() : SSTglobal;
  12369. wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, NULL, value, 1, 0, StatsMergeAppend);
  12370. }
  12371. virtual void updateStatistic(StatisticKind kind, unsigned __int64 value, StatsMergeAction mergeAction)
  12372. {
  12373. StatisticScopeType scopeType = scopeTypeStack.ordinality() ? (StatisticScopeType)scopeTypeStack.tos() : SSTglobal;
  12374. wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, NULL, value, 1, 0, mergeAction);
  12375. }
  12376. virtual IStatisticCollection * getResult()
  12377. {
  12378. return NULL;
  12379. }
  12380. protected:
  12381. Linked<IWorkUnit> wu;
  12382. StringBuffer scope;
  12383. UnsignedArray prevLenStack;
  12384. UnsignedArray scopeTypeStack;
  12385. };
  12386. IStatisticGatherer * createGlobalStatisticGatherer(IWorkUnit * wu)
  12387. {
  12388. return new GlobalStatisticGatherer(wu);
  12389. }
  12390. extern WORKUNIT_API IPropertyTree * getWUGraphProgress(const char * wuid, bool readonly)
  12391. {
  12392. if (!wuid || !*wuid)
  12393. return NULL;
  12394. VStringBuffer path("/GraphProgress/%s", wuid);
  12395. Owned<IRemoteConnection> conn = querySDS().connect(path.str(),myProcessSession(),readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
  12396. if (conn)
  12397. return conn->getRoot();
  12398. else
  12399. return NULL;
  12400. }
  12401. void addWorkunitException(IWorkUnit * wu, IError * error, bool removeTimeStamp)
  12402. {
  12403. ErrorSeverity wuSeverity = SeverityInformation;
  12404. ErrorSeverity severity = error->getSeverity();
  12405. switch (severity)
  12406. {
  12407. case SeverityIgnore:
  12408. return;
  12409. case SeverityInformation:
  12410. break;
  12411. case SeverityWarning:
  12412. wuSeverity = SeverityWarning;
  12413. break;
  12414. case SeverityError:
  12415. case SeverityFatal:
  12416. wuSeverity = SeverityError;
  12417. break;
  12418. }
  12419. Owned<IWUException> exception = wu->createException();
  12420. exception->setSeverity(wuSeverity);
  12421. StringBuffer msg;
  12422. exception->setExceptionCode(error->errorCode());
  12423. exception->setExceptionMessage(error->errorMessage(msg).str());
  12424. const char * source = queryCreatorTypeName(queryStatisticsComponentType());
  12425. exception->setExceptionSource(source);
  12426. exception->setExceptionFileName(error->getFilename());
  12427. exception->setExceptionLineNo(error->getLine());
  12428. exception->setExceptionColumn(error->getColumn());
  12429. if (removeTimeStamp)
  12430. exception->setTimeStamp(nullptr);
  12431. if (error->getActivity())
  12432. exception->setActivityId(error->getActivity());
  12433. if (error->queryScope())
  12434. exception->setScope(error->queryScope());
  12435. }
  12436. IError * WorkUnitErrorReceiver::mapError(IError * error)
  12437. {
  12438. return LINK(error);
  12439. }
  12440. void WorkUnitErrorReceiver::report(IError* eclError)
  12441. {
  12442. addWorkunitException(wu, eclError, removeTimeStamp);
  12443. }
  12444. size32_t WorkUnitErrorReceiver::errCount()
  12445. {
  12446. unsigned count = 0;
  12447. Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
  12448. ForEach(*exceptions)
  12449. if (exceptions->query().getSeverity() == SeverityError)
  12450. count++;
  12451. return count;
  12452. }
  12453. size32_t WorkUnitErrorReceiver::warnCount()
  12454. {
  12455. unsigned count = 0;
  12456. Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
  12457. ForEach(*exceptions)
  12458. if (exceptions->query().getSeverity() == SeverityWarning)
  12459. count++;
  12460. return count;
  12461. }
  12462. bool isValidPriorityValue(const char *priority)
  12463. {
  12464. if (isEmptyString(priority))
  12465. return false;
  12466. if (strieq("SLA", priority) || strieq("LOW", priority) || strieq("HIGH", priority) || strieq("NONE", priority))
  12467. return true;
  12468. return false;
  12469. }
  12470. bool isValidMemoryValue(const char *memoryUnit)
  12471. {
  12472. if (isEmptyString(memoryUnit) || !isdigit(*memoryUnit))
  12473. return false;
  12474. while (isdigit(*++memoryUnit));
  12475. if (!*memoryUnit)
  12476. return true;
  12477. switch (toupper(*memoryUnit++))
  12478. {
  12479. case 'E':
  12480. case 'P':
  12481. case 'T':
  12482. case 'G':
  12483. case 'M':
  12484. case 'K':
  12485. if (!*memoryUnit || strieq("B", memoryUnit))
  12486. return true;
  12487. break;
  12488. case 'B':
  12489. if (!*memoryUnit)
  12490. return true;
  12491. break;
  12492. }
  12493. return false;
  12494. }
  12495. // used by eclagent and roxie
  12496. #define ABORT_POLL_PERIOD (5*1000)
  12497. void executeThorGraph(const char * graphName, IConstWorkUnit &workunit, const IPropertyTree &config)
  12498. {
  12499. unsigned timelimit = workunit.getDebugValueInt("thorConnectTimeout", config.getPropInt("@thorConnectTimeout", 60));
  12500. StringAttr wuid(workunit.queryWuid());
  12501. IConstWUGraph *graph = workunit.getGraph(graphName);
  12502. if (!graph)
  12503. throw makeStringExceptionV(0, "getGraph() returns nullptr for %s", graphName);
  12504. unsigned wfid = graph->getWfid();
  12505. #ifdef _CONTAINERIZED
  12506. // NB: If a single agent were to want to launch >1 Thor, then the threading could be in the workflow above this call.
  12507. {
  12508. Owned<IWorkUnit> w = &workunit.lock();
  12509. w->setState(WUStateBlocked);
  12510. }
  12511. WUState state = WUStateUnknown;
  12512. if (config.hasProp("@queue"))
  12513. {
  12514. CCycleTimer elapsedTimer;
  12515. bool multiJobLinger = config.getPropBool("@multiJobLinger");
  12516. if (executeGraphOnLingeringThor(workunit, graphName, multiJobLinger ? config.queryProp("@queue") : nullptr))
  12517. PROGLOG("Existing lingering Thor handled graph: %s", graphName);
  12518. else
  12519. {
  12520. VStringBuffer queueName("%s.thor", config.queryProp("@queue"));
  12521. DBGLOG("Queueing wuid=%s, graph=%s, on queue=%s, timelimit=%u seconds", wuid.str(), graphName, queueName.str(), timelimit);
  12522. Owned<IJobQueue> queue = createJobQueue(queueName);
  12523. VStringBuffer jobName("%s/%s", wuid.get(), graphName);
  12524. IJobQueueItem *item = createJobQueueItem(jobName);
  12525. queue->enqueue(item);
  12526. }
  12527. // NB: overall max runtime if guillotine set handled by abortmonitor
  12528. unsigned runningTimeLimit = workunit.getDebugValueInt("maxRunTime", 0);
  12529. runningTimeLimit = runningTimeLimit ? runningTimeLimit : INFINITE;
  12530. std::list<WUState> expectedStates = { WUStateRunning, WUStateWait };
  12531. unsigned __int64 blockedTime = 0;
  12532. for (unsigned i=0; i<2; i++)
  12533. {
  12534. WUState state = waitForWorkUnitToComplete(wuid, timelimit*1000, expectedStates);
  12535. DBGLOG("Got state: %s", getWorkunitStateStr(state));
  12536. if (WUStateWait == state) // already finished
  12537. {
  12538. // workunit may have spent time in blocked state, but then transitioned to
  12539. // wait state quickly such that this code did not see its running state.
  12540. if (!blockedTime)
  12541. blockedTime = elapsedTimer.elapsedNs();
  12542. break;
  12543. }
  12544. else if ((INFINITE != timelimit) && (WUStateUnknown == state))
  12545. throw makeStringExceptionV(0, "Query %s failed to start within specified timelimit (%u) seconds", wuid.str(), timelimit);
  12546. else
  12547. {
  12548. auto it = std::find(expectedStates.begin(), expectedStates.end(), state);
  12549. if (it == expectedStates.end())
  12550. throw makeStringExceptionV(0, "Query %s failed, state: %s", wuid.str(), getWorkunitStateStr(state));
  12551. }
  12552. blockedTime = elapsedTimer.elapsedNs();
  12553. timelimit = runningTimeLimit;
  12554. expectedStates = { WUStateWait };
  12555. }
  12556. Owned<IWorkUnit> w = &workunit.lock();
  12557. updateWorkunitStat(w, SSTgraph, graphName, StTimeBlocked, nullptr, blockedTime, wfid);
  12558. }
  12559. else
  12560. {
  12561. VStringBuffer job("%s-%s", wuid.str(), graphName);
  12562. runK8sJob("thormanager", wuid, job, { { "graphName", graphName} });
  12563. }
  12564. /* In k8s, Thor feeds back the terminating exception via the workunit.
  12565. * (in bare-metal it is returned via a socket the agent connect to Thor with)
  12566. */
  12567. workunit.forceReload();
  12568. if (workunit.getExceptionCount())
  12569. {
  12570. Owned<IConstWUExceptionIterator> iter = &workunit.getExceptions();
  12571. ForEach(*iter)
  12572. {
  12573. IConstWUException &e = iter->query();
  12574. SCMStringBuffer str;
  12575. e.getExceptionSource(str);
  12576. if (streq("thormasterexception", str.s))
  12577. {
  12578. str.clear();
  12579. e.getExceptionMessage(str);
  12580. unsigned errCode = e.getExceptionCode();
  12581. {
  12582. /* Clear the feedback exception (consume it),
  12583. * so that any future re-submissions of this workunit (e.g. due to workflow)
  12584. * don't see it again.
  12585. */
  12586. Owned<IWorkUnit> w = &workunit.lock();
  12587. w->clearExceptions("thormasterexception");
  12588. }
  12589. throw makeStringException(errCode, str.str());
  12590. }
  12591. }
  12592. }
  12593. else
  12594. {
  12595. Owned<IWorkUnit> w = &workunit.lock();
  12596. WUState state = w->getState();
  12597. if (WUStateFailed == state)
  12598. throw makeStringException(0, "Workunit failed");
  12599. }
  12600. {
  12601. Owned<IWorkUnit> w = &workunit.lock();
  12602. w->setState(WUStateRunning);
  12603. }
  12604. #else
  12605. StringAttr owner(workunit.queryUser());
  12606. StringAttr cluster(workunit.queryClusterName());
  12607. int priority = workunit.getPriorityValue();
  12608. Owned<IConstWUClusterInfo> c = getTargetClusterInfo(cluster);
  12609. if (!c)
  12610. throw MakeStringException(0, "Invalid thor cluster %s", cluster.str());
  12611. SCMStringBuffer queueName;
  12612. c->getThorQueue(queueName);
  12613. Owned<IJobQueue> jq = createJobQueue(queueName.str());
  12614. bool resubmit;
  12615. Owned<IWorkUnitFactory> wuFactory = getWorkUnitFactory();
  12616. do // loop if pause interrupted graph and needs resubmitting on resume
  12617. {
  12618. resubmit = false; // set if job interrupted in thor
  12619. if (WUStatePaused == workunit.getState()) // check initial state - and wait if paused
  12620. {
  12621. for (;;)
  12622. {
  12623. WUAction action = wuFactory->waitForWorkUnitAction(wuid, workunit.getAction());
  12624. if (action == WUActionUnknown)
  12625. throw new WorkflowException(0, "Workunit aborting", 0, WorkflowException::ABORT, MSGAUD_user);
  12626. if (action != WUActionPause && action != WUActionPauseNow)
  12627. break;
  12628. }
  12629. }
  12630. {
  12631. Owned<IWorkUnit> w = &workunit.lock();
  12632. w->setState(WUStateBlocked);
  12633. }
  12634. class cPollThread: public Thread // MORE - why do we ned a thread here?
  12635. {
  12636. Semaphore sem;
  12637. bool stopped;
  12638. IJobQueue *jq;
  12639. IConstWorkUnit *wu;
  12640. public:
  12641. bool timedout;
  12642. CTimeMon tm;
  12643. cPollThread(IJobQueue *_jq, IConstWorkUnit *_wu, unsigned timelimit)
  12644. : tm(timelimit)
  12645. {
  12646. stopped = false;
  12647. jq = _jq;
  12648. wu = _wu;
  12649. timedout = false;
  12650. }
  12651. ~cPollThread()
  12652. {
  12653. stop();
  12654. }
  12655. int run()
  12656. {
  12657. while (!stopped) {
  12658. sem.wait(ABORT_POLL_PERIOD);
  12659. if (stopped)
  12660. break;
  12661. if (tm.timedout()) {
  12662. timedout = true;
  12663. stopped = true;
  12664. jq->cancelInitiateConversation();
  12665. }
  12666. else if (wu->aborting()) {
  12667. stopped = true;
  12668. jq->cancelInitiateConversation();
  12669. }
  12670. }
  12671. return 0;
  12672. }
  12673. void stop()
  12674. {
  12675. stopped = true;
  12676. sem.signal();
  12677. }
  12678. } pollthread(jq, &workunit, timelimit*1000);
  12679. pollthread.start();
  12680. CCycleTimer elapsedTimer;
  12681. PROGLOG("Enqueuing on %s to run wuid=%s, graph=%s, timelimit=%d seconds, priority=%d", queueName.str(), wuid.str(), graphName, timelimit, priority);
  12682. VStringBuffer wuidGraph("%s/%s", wuid.str(), graphName);
  12683. IJobQueueItem* item = createJobQueueItem(wuidGraph.str());
  12684. item->setOwner(owner.str());
  12685. item->setPriority(priority);
  12686. Owned<IConversation> conversation = jq->initiateConversation(item);
  12687. bool got = conversation.get()!=NULL;
  12688. pollthread.stop();
  12689. pollthread.join();
  12690. if (!got)
  12691. {
  12692. if (pollthread.timedout)
  12693. throw MakeStringException(0, "Query %s failed to start within specified timelimit (%u) seconds", wuidGraph.str(), timelimit);
  12694. throw MakeStringException(0, "Query %s cancelled (1)", wuidGraph.str());
  12695. }
  12696. // get the thor ep from whoever picked up
  12697. SocketEndpoint thorMaster;
  12698. MemoryBuffer msg;
  12699. if (!conversation->recv(msg,1000*60))
  12700. throw MakeStringException(0, "Query %s cancelled (2)", wuidGraph.str());
  12701. thorMaster.deserialize(msg);
  12702. msg.clear();
  12703. SocketEndpoint myep;
  12704. myep.setLocalHost(0);
  12705. myep.serialize(msg); // only used for tracing
  12706. if (!conversation->send(msg)) {
  12707. StringBuffer s("Failed to send query to Thor on ");
  12708. thorMaster.getUrlStr(s);
  12709. throw MakeStringExceptionDirect(-1, s.str()); // maybe retry?
  12710. }
  12711. unsigned __int64 blockedTime = elapsedTimer.elapsedNs();
  12712. {
  12713. Owned<IWorkUnit> w = &workunit.lock();
  12714. updateWorkunitStat(w, SSTgraph, graphName, StTimeBlocked, nullptr, blockedTime, wfid);
  12715. }
  12716. StringBuffer eps;
  12717. PROGLOG("Thor on %s running %s", thorMaster.getUrlStr(eps).str(), wuidGraph.str());
  12718. MemoryBuffer reply;
  12719. try
  12720. {
  12721. if (!conversation->recv(reply,INFINITE))
  12722. {
  12723. StringBuffer s("Failed to receive reply from thor ");
  12724. thorMaster.getUrlStr(s);
  12725. throw MakeStringExceptionDirect(-1, s.str());
  12726. }
  12727. }
  12728. catch (IException *e)
  12729. {
  12730. StringBuffer s("Failed to receive reply from thor ");
  12731. thorMaster.getUrlStr(s);
  12732. s.append("; (").append(e->errorCode()).append(", ");
  12733. e->errorMessage(s).append(")");
  12734. e->Release();
  12735. throw MakeStringExceptionDirect(-1, s.str());
  12736. }
  12737. unsigned replyCode;
  12738. reply.read(replyCode);
  12739. switch ((ThorReplyCodes) replyCode)
  12740. {
  12741. case DAMP_THOR_REPLY_PAUSED:
  12742. {
  12743. bool isException;
  12744. reply.read(isException);
  12745. if (isException)
  12746. {
  12747. Owned<IException> e = deserializeException(reply);
  12748. VStringBuffer str("Pausing job %s caused exception", wuidGraph.str());
  12749. EXCLOG(e, str.str());
  12750. }
  12751. Owned<IWorkUnit> w = &workunit.lock();
  12752. w->setState(WUStatePaused); // will trigger executeThorGraph to pause next time around.
  12753. WUAction action = w->getAction();
  12754. switch (action)
  12755. {
  12756. case WUActionPause:
  12757. case WUActionPauseNow:
  12758. w->setAction(WUActionUnknown);
  12759. }
  12760. resubmit = true; // JCSMORE - all subgraph _could_ be done, thor will check though and not rerun
  12761. break;
  12762. }
  12763. case DAMP_THOR_REPLY_GOOD:
  12764. break;
  12765. case DAMP_THOR_REPLY_ERROR:
  12766. {
  12767. throw deserializeException(reply);
  12768. }
  12769. case DAMP_THOR_REPLY_ABORT:
  12770. {
  12771. Owned<IException> e = deserializeException(reply);
  12772. StringBuffer msg;
  12773. e->errorMessage(msg);
  12774. throw new WorkflowException(e->errorCode(), msg.str(), 0, WorkflowException::ABORT, MSGAUD_user);
  12775. }
  12776. default:
  12777. throwUnexpected();
  12778. }
  12779. workunit.forceReload();
  12780. }
  12781. while (resubmit); // if pause interrupted job (i.e. with pausenow action), resubmit graph
  12782. #endif
  12783. }
  12784. #ifdef _CONTAINERIZED
  12785. bool executeGraphOnLingeringThor(IConstWorkUnit &workunit, const char *graphName, const char *multiJobLingerQueueName)
  12786. {
  12787. // check if lingering thor instance is up.
  12788. // Returns true if successfully submitted graph to a lingering Thor.
  12789. /* If code was dependent on reading a workunit in parallel, the whole area of
  12790. * workunit locking and reading will need revisiting, because at the moment the
  12791. * workunit reading code does not lock at all.
  12792. *
  12793. * Either,
  12794. * 1) multiJobThorLinger is set, meaning Thor instances will wait for any job's graph.
  12795. * OR
  12796. * 2) if not set, the Thor will wait for graphs from same job only, and will be
  12797. * notified to quit by the agent, when the last graph is complete.
  12798. *
  12799. * This code is called from the client (agent) when it wants to execute a Thor graph.
  12800. * This function checks to see if there any registered lingering Thor instances available.
  12801. *
  12802. * Lingering Thor's register when they are idle either with the workunit itself in
  12803. * the case of [2], or with a Dali psuedo queue (a branch under /Status/ThorLinger),
  12804. * in the case of [1]
  12805. * If they have not been contacted within the 'lingerPeriod' they shutdown and remove
  12806. * their registered instance from the queue.
  12807. *
  12808. * The client will attempt to find a lingering Thor in either the workunit or a global
  12809. * named dali queue (depending on the configuration), it will then remove that entry,
  12810. * and attempt to post the graph to that lingering Thor. If the Thor acknowledges the
  12811. * request, this function exits with success. If it it fails, or there are no more
  12812. * lingering Thor instances, it returns with false to indicate that the graph was not
  12813. * submitted to a lingering Thor.
  12814. * The client should then queue the wuid/graph normally, which will invoke a new Thor
  12815. * instance.
  12816. */
  12817. try
  12818. {
  12819. if (multiJobLingerQueueName && graphName)
  12820. {
  12821. // check a Dali queue for idle spare Thor's
  12822. DBGLOG("Checking multi job lingering queue: %s", multiJobLingerQueueName);
  12823. VStringBuffer xpath("/Status/ThorLinger/%s", multiJobLingerQueueName);
  12824. while (true)
  12825. {
  12826. Owned<IRemoteConnection> conn = querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE, 5*60*1000);
  12827. if (!conn)
  12828. break;
  12829. IPropertyTree *root = conn->queryRoot();
  12830. if (0 == root->numChildren())
  12831. break;
  12832. IPropertyTree *entry = root->queryPropTree("Thor*[1]");
  12833. const char *entryName = entry->queryName();
  12834. // get available instance and remove it
  12835. VStringBuffer entryXPath("%s/%s", xpath.str(), entryName);
  12836. Owned<IRemoteConnection> lingerInstanceConn = querySDS().connect(entryXPath, myProcessSession(), RTM_LOCK_WRITE | RTM_DELETE_ON_DISCONNECT, 5*1000);
  12837. if (lingerInstanceConn)
  12838. {
  12839. StringBuffer instanceName;
  12840. lingerInstanceConn->queryRoot()->getProp(nullptr, instanceName);
  12841. lingerInstanceConn->close(true);
  12842. lingerInstanceConn.clear();
  12843. conn.clear();
  12844. Owned<INode> masterNode = createINode(instanceName);
  12845. CMessageBuffer msg;
  12846. VStringBuffer jobStr("%s/%s", workunit.queryWuid(), graphName);
  12847. msg.append(jobStr);
  12848. bool success = queryWorldCommunicator().sendRecv(msg, masterNode, MPTAG_THOR, 10000);
  12849. DBGLOG("Found lingering Thor: %s, submitted: %s, success: %s", instanceName.str(), jobStr.str(), boolToStr(success));
  12850. if (success)
  12851. {
  12852. bool ok;
  12853. msg.read(ok);
  12854. return true;
  12855. }
  12856. }
  12857. }
  12858. }
  12859. else
  12860. {
  12861. /* NB: forcing a reload here to ensure Debug values are recent.
  12862. * Could be improved by refreshing only the specific area of interest (i.e. Debug/ in this case).
  12863. * The area of persisting a non-locked connection to workunits should be resivisted..
  12864. */
  12865. workunit.forceReload();
  12866. Owned<IStringIterator> iter = &workunit.getDebugValues("thorinstance_*");
  12867. ForEach(*iter)
  12868. {
  12869. /* NB: Thor's set their running endpoint into Debug values of workunit
  12870. * Check to see if workunit has any Thor's available lingering instances.
  12871. */
  12872. SCMStringBuffer thorInstance;
  12873. iter->str(thorInstance);
  12874. SCMStringBuffer thorInstanceValue;
  12875. if (workunit.getDebugValueBool(thorInstance.s, false))
  12876. {
  12877. {
  12878. Owned<IWorkUnit> w = &workunit.lock();
  12879. w->setDebugValue(thorInstance.s, "0", true);
  12880. }
  12881. /* NB: there's a window where Thor could shutdown here.
  12882. * In that case, the sendRecv will fail and it will fall through to queueing.
  12883. */
  12884. const char *instanceName = strchr(thorInstance.str(), '_') + 1;
  12885. Owned<INode> masterNode = createINode(instanceName);
  12886. CMessageBuffer msg;
  12887. VStringBuffer jobStr("%s/%s", workunit.queryWuid(), graphName?graphName:"");
  12888. msg.append(jobStr);
  12889. if (queryWorldCommunicator().sendRecv(msg, masterNode, MPTAG_THOR, 10000))
  12890. {
  12891. bool ok;
  12892. msg.read(ok);
  12893. if (graphName) // if graphName==nullptr, then continue around to look at others
  12894. {
  12895. if (ok)
  12896. return true;
  12897. }
  12898. }
  12899. }
  12900. }
  12901. }
  12902. }
  12903. catch (IException *e)
  12904. {
  12905. EXCLOG(e, "executeGraphOnLingeringThor");
  12906. e->Release();
  12907. }
  12908. return false;
  12909. }
  12910. static void setResources(IPropertyTree *workerConfig, const IConstWorkUnit *workunit, const char *process)
  12911. {
  12912. auto setResourcesItem = [&workerConfig](const char *category, const char *resourceName, unsigned value, const char *units)
  12913. {
  12914. if (!value) return;
  12915. VStringBuffer xpath("spec/template/spec/containers/resources/%s@%s", category, resourceName);
  12916. ensurePTree(workerConfig, xpath.str());
  12917. VStringBuffer v("%u%s", value, units);
  12918. workerConfig->setProp(xpath.str(), v.str());
  12919. };
  12920. StringBuffer s;
  12921. unsigned memRequest = workunit->getDebugValueInt(s.clear().appendf("resource-%s-memory", process), 0);
  12922. setResourcesItem("requests", "memory", memRequest, "Mi");
  12923. setResourcesItem("limits", "memory", memRequest, "Mi");
  12924. unsigned cpuRequest = workunit->getDebugValueInt(s.clear().appendf("resource-%s-cpu", process), 0);
  12925. setResourcesItem("requests", "cpu", cpuRequest, "m");
  12926. setResourcesItem("limits", "cpu", cpuRequest, "m");
  12927. }
  12928. KeepK8sJobs translateKeepJobs(const char *keepJob)
  12929. {
  12930. if (!isEmptyString(keepJob)) // common case
  12931. {
  12932. if (streq("podfailures", keepJob))
  12933. return KeepK8sJobs::podfailures;
  12934. else if (streq("all", keepJob))
  12935. return KeepK8sJobs::all;
  12936. }
  12937. return KeepK8sJobs::none;
  12938. }
  12939. // NB: will fire an exception if command fails (returns non-zero exit code)
  12940. static void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output)
  12941. {
  12942. StringBuffer _output, error;
  12943. if (!output)
  12944. output = &_output;
  12945. unsigned ret = runExternalCommand(title, *output, error, cmd, input, ".", nullptr);
  12946. if (output->length())
  12947. MLOG(MCExtraneousInfo, unknownJob, "%s: ret=%u, stdout=%s", cmd, ret, output->trimRight().str());
  12948. if (error.length())
  12949. MLOG(MCinternalError, unknownJob, "%s: ret=%u, stderr=%s", cmd, ret, error.trimRight().str());
  12950. if (ret)
  12951. {
  12952. if (input)
  12953. MLOG(MCinternalError, unknownJob, "Using input %s", input);
  12954. throw makeStringExceptionV(0, "Failed to run %s: error %u: %s", cmd, ret, error.str());
  12955. }
  12956. }
  12957. bool isActiveK8sService(const char *serviceName)
  12958. {
  12959. VStringBuffer getEndpoints("kubectl get endpoints %s \"--output=jsonpath={range .subsets[*].addresses[*]}{.ip}{'\\n'}{end}\"", serviceName);
  12960. StringBuffer output;
  12961. runKubectlCommand("checkEndpoints", getEndpoints.str(), nullptr, &output);
  12962. // Output should be zero or more lines each with an IP
  12963. return (output.length() && output.charAt(0) != '\n');
  12964. }
  12965. void deleteK8sResource(const char *componentName, const char *job, const char *resource)
  12966. {
  12967. VStringBuffer jobname("%s-%s", componentName, job);
  12968. jobname.toLowerCase();
  12969. VStringBuffer deleteResource("kubectl delete %s/%s", resource, jobname.str());
  12970. runKubectlCommand(componentName, deleteResource, nullptr, nullptr);
  12971. }
  12972. void waitK8sJob(const char *componentName, const char *job, unsigned pendingTimeoutSecs, KeepK8sJobs keepJob)
  12973. {
  12974. VStringBuffer jobname("%s-%s", componentName, job);
  12975. jobname.toLowerCase();
  12976. VStringBuffer waitJob("kubectl get jobs %s -o jsonpath={.status.active}", jobname.str());
  12977. VStringBuffer getScheduleStatus("kubectl get pods --selector=job-name=%s --output=jsonpath={.items[*].status.conditions[?(@.type=='PodScheduled')].status}", jobname.str());
  12978. VStringBuffer checkJobExitCode("kubectl get pods --selector=job-name=%s --output=jsonpath={.items[*].status.containerStatuses[?(@.name==\"%s\")].state.terminated.exitCode}", jobname.str(), jobname.str());
  12979. unsigned delay = 100;
  12980. unsigned start = msTick();
  12981. bool schedulingTimeout = false;
  12982. Owned<IException> exception;
  12983. try
  12984. {
  12985. for (;;)
  12986. {
  12987. StringBuffer output;
  12988. runKubectlCommand(componentName, waitJob, nullptr, &output);
  12989. if (!streq(output, "1")) // status.active value
  12990. {
  12991. // Job is no longer active - we can terminate
  12992. DBGLOG("kubectl jobs output: %s", output.str());
  12993. runKubectlCommand(componentName, checkJobExitCode, nullptr, &output.clear());
  12994. if (output.length() && !streq(output, "0")) // state.terminated.exitCode
  12995. throw makeStringExceptionV(0, "Failed to run %s: pod exited with error: %s", jobname.str(), output.str());
  12996. break;
  12997. }
  12998. runKubectlCommand(nullptr, getScheduleStatus, nullptr, &output.clear());
  12999. // Check whether pod has been scheduled yet - if resources are not available pods may block indefinitely waiting to be scheduled, and
  13000. // we would prefer them to fail instead.
  13001. bool pending = streq(output, "False");
  13002. if (pendingTimeoutSecs && pending && msTick()-start > pendingTimeoutSecs*1000)
  13003. {
  13004. schedulingTimeout = true;
  13005. VStringBuffer getReason("kubectl get pods --selector=job-name=%s \"--output=jsonpath={range .items[*].status.conditions[?(@.type=='PodScheduled')]}{.reason}{': '}{.message}{end}\"", jobname.str());
  13006. runKubectlCommand(componentName, getReason, nullptr, &output.clear());
  13007. throw makeStringExceptionV(0, "Failed to run %s - pod not scheduled after %u seconds: %s ", jobname.str(), pendingTimeoutSecs, output.str());
  13008. }
  13009. MilliSleep(delay);
  13010. if (delay < 10000)
  13011. delay = delay * 2;
  13012. }
  13013. }
  13014. catch (IException *e)
  13015. {
  13016. EXCLOG(e, nullptr);
  13017. exception.setown(e);
  13018. }
  13019. if (keepJob != KeepK8sJobs::all)
  13020. {
  13021. // Delete jobs unless the pod failed and keepJob==podfailures
  13022. if ((nullptr == exception) || (KeepK8sJobs::podfailures != keepJob) || schedulingTimeout)
  13023. deleteK8sResource(componentName, job, "job");
  13024. }
  13025. if (exception)
  13026. throw exception.getClear();
  13027. }
  13028. bool applyK8sYaml(const char *componentName, const char *wuid, const char *job, const char *suffix, const std::list<std::pair<std::string, std::string>> &extraParams, bool optional)
  13029. {
  13030. StringBuffer jobname(job);
  13031. jobname.toLowerCase();
  13032. VStringBuffer jobSpecFilename("/etc/config/%s-%s.yaml", componentName, suffix);
  13033. StringBuffer jobYaml;
  13034. try
  13035. {
  13036. jobYaml.loadFile(jobSpecFilename, false);
  13037. }
  13038. catch (IException *E)
  13039. {
  13040. if (!optional)
  13041. throw;
  13042. E->Release();
  13043. return false;
  13044. }
  13045. jobYaml.replaceString("_HPCC_JOBNAME_", jobname.str());
  13046. VStringBuffer args("\"--workunit=%s\"", wuid);
  13047. for (const auto &p: extraParams)
  13048. {
  13049. if (hasPrefix(p.first.c_str(), "_HPCC_", false)) // jobspec substituion
  13050. jobYaml.replaceString(p.first.c_str(), p.second.c_str());
  13051. else
  13052. args.append(" \"--").append(p.first.c_str()).append('=').append(p.second.c_str()).append("\"");
  13053. }
  13054. jobYaml.replaceString("_HPCC_ARGS_", args.str());
  13055. // Disable ability change resources from within workunit
  13056. // - all values are unquoted by toYAML. This caused problems when previous string values are
  13057. // outputted unquoted and then treated as a non string type -e.g. labels in metadata.
  13058. // - Also, ability to control if and how much users may change resources should be provided.
  13059. #if 0
  13060. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  13061. if (factory)
  13062. {
  13063. Owned<IConstWorkUnit> workunit = factory->openWorkUnit(wuid);
  13064. if (workunit)
  13065. {
  13066. Owned<IPropertyTree> workerConfig = createPTreeFromYAMLString(jobYaml.length(), jobYaml.str(), 0, ptr_none, nullptr);
  13067. setResources(workerConfig, workunit, componentName);
  13068. toYAML(workerConfig, jobYaml.clear(), 2, 0);
  13069. }
  13070. }
  13071. #endif
  13072. runKubectlCommand(componentName, "kubectl replace --force -f -", jobYaml, nullptr);
  13073. return true;
  13074. }
  13075. static constexpr unsigned defaultPendingTimeSecs = 600;
  13076. void runK8sJob(const char *componentName, const char *wuid, const char *job, const std::list<std::pair<std::string, std::string>> &extraParams)
  13077. {
  13078. Owned<IPropertyTree> compConfig = getComponentConfig();
  13079. KeepK8sJobs keepJob = translateKeepJobs(compConfig->queryProp("@keepJobs"));
  13080. unsigned pendingTimeoutSecs = compConfig->getPropInt("@pendingTimeoutSecs", defaultPendingTimeSecs);
  13081. bool removeNetwork = applyK8sYaml(componentName, wuid, job, "networkspec", extraParams, true);
  13082. applyK8sYaml(componentName, wuid, job, "jobspec", extraParams, false);
  13083. Owned<IException> exception;
  13084. try
  13085. {
  13086. waitK8sJob(componentName, job, pendingTimeoutSecs, keepJob);
  13087. }
  13088. catch (IException *e)
  13089. {
  13090. EXCLOG(e, nullptr);
  13091. exception.setown(e);
  13092. }
  13093. if (removeNetwork)
  13094. deleteK8sResource(componentName, job, "networkpolicy");
  13095. if (exception)
  13096. throw exception.getClear();
  13097. }
  13098. std::pair<std::string, unsigned> getExternalService(const char *serviceName)
  13099. {
  13100. static CTimeLimitedCache<std::string, std::pair<std::string, unsigned>> externalServiceCache;
  13101. static CriticalSection externalServiceCacheCrit;
  13102. {
  13103. CriticalBlock b(externalServiceCacheCrit);
  13104. std::pair<std::string, unsigned> cachedExternalSevice;
  13105. if (externalServiceCache.get(serviceName, cachedExternalSevice))
  13106. return cachedExternalSevice;
  13107. }
  13108. StringBuffer output;
  13109. try
  13110. {
  13111. VStringBuffer getServiceCmd("kubectl get svc --selector=server=%s --output=jsonpath={.items[0].status.loadBalancer.ingress[0].hostname},{.items[0].status.loadBalancer.ingress[0].ip},{.items[0].spec.ports[0].port}", serviceName);
  13112. runKubectlCommand("get-external-service", getServiceCmd, nullptr, &output);
  13113. }
  13114. catch (IException *e)
  13115. {
  13116. EXCLOG(e);
  13117. VStringBuffer exceptionText("Failed to get external service for '%s'. Error: [%d, ", serviceName, e->errorCode());
  13118. e->errorMessage(exceptionText).append("]");
  13119. e->Release();
  13120. throw makeStringException(-1, exceptionText);
  13121. }
  13122. StringArray fields;
  13123. fields.appendList(output, ",");
  13124. // NB: add even if no result, want non-result to be cached too
  13125. std::string host, port;
  13126. if (fields.ordinality() == 3) // hostname,ip,port. NB: hostname may be missing, but still present as a blank field
  13127. {
  13128. host = fields.item(0); // hostname
  13129. if (0 == host.length())
  13130. host = fields.item(1); // ip
  13131. port = fields.item(2);
  13132. }
  13133. auto servicePair = std::make_pair(host, atoi(port.c_str()));
  13134. externalServiceCache.add(serviceName, servicePair);
  13135. return servicePair;
  13136. }
  13137. // returns a vector of {pod-name, node-name} vectors,
  13138. // represented as a nested vector for extensibility, e.g. to add other meta fields
  13139. std::vector<std::vector<std::string>> getPodNodes(const char *selector)
  13140. {
  13141. VStringBuffer getWorkerNodes("kubectl get pods --selector=job-name=%s \"--output=jsonpath={range .items[*]}{.metadata.name},{.spec.nodeName}{'\\n'}{end}\"", selector);
  13142. StringBuffer result;
  13143. runKubectlCommand("get-worker-nodes", getWorkerNodes, nullptr, &result);
  13144. if (result.isEmpty())
  13145. throw makeStringExceptionV(-1, "No worker nodes found for selector '%s'", selector);
  13146. const char *start = result.str();
  13147. const char *finger = start;
  13148. std::string fieldName;
  13149. std::vector<std::vector<std::string>> results;
  13150. std::vector<std::string> current;
  13151. while (true)
  13152. {
  13153. switch (*finger)
  13154. {
  13155. case ',':
  13156. {
  13157. if (start == finger)
  13158. throw makeStringException(-1, "getPodNodes: Missing node name(s) in output");
  13159. fieldName.assign(start, finger-start);
  13160. current.emplace_back(std::move(fieldName));
  13161. finger++;
  13162. start = finger;
  13163. break;
  13164. }
  13165. case '\n':
  13166. case '\0':
  13167. {
  13168. if (start == finger)
  13169. throw makeStringException(-1, "getPodNodes: Missing pod name(s) in output");
  13170. fieldName.assign(start, finger-start);
  13171. current.emplace_back(std::move(fieldName));
  13172. results.emplace_back(std::move(current));
  13173. if ('\0' == *finger)
  13174. return results;
  13175. finger++;
  13176. start = finger;
  13177. break;
  13178. }
  13179. default:
  13180. {
  13181. ++finger;
  13182. break;
  13183. }
  13184. }
  13185. }
  13186. }
  13187. #endif