1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include <string>
- #include <unordered_set>
- #include "jlib.hpp"
- #include "workunit.hpp"
- #include "jprop.hpp"
- #include "jmisc.hpp"
- #include "jexcept.hpp"
- #include "jiter.ipp"
- #include "jptree.hpp"
- #include "jtime.ipp"
- #include "jencrypt.hpp"
- #include "junicode.hpp"
- #include "jlzw.hpp"
- #include "jregexp.hpp"
- #include "eclrtl.hpp"
- #include "deftype.hpp"
- #include <time.h>
- #include "mpbase.hpp"
- #include "daclient.hpp"
- #include "dadfs.hpp"
- #include "dafdesc.hpp"
- #include "dasds.hpp"
- #include "danqs.hpp"
- #include "dautils.hpp"
- #include "dllserver.hpp"
- #include "thorplugin.hpp"
- #include "thorhelper.hpp"
- #include "workflow.hpp"
- #include "nbcd.hpp"
- #include "seclib.hpp"
- #include "wuerror.hpp"
- #include "wujobq.hpp"
- #ifndef _CONTAINERIZED
- #include "environment.hpp"
- #endif
- #include "daqueue.hpp"
- #include "workunit.ipp"
- #include "digisign.hpp"
- #include <list>
- #include <string>
- #include <algorithm>
- using namespace cryptohelper;
- static int workUnitTraceLevel = 1;
- static StringBuffer &getXPath(StringBuffer &wuRoot, const char *wuid)
- {
- // MORE - can fold in the date
- return wuRoot.append("/WorkUnits/").append(wuid);
- }
- //To be called by eclserver, but esp etc. won't know, so we need to store it.
- static StringBuffer & appendLibrarySuffix(StringBuffer & suffix)
- {
- #ifdef _WIN32
- suffix.append("W");
- #else
- suffix.append("L");
- #endif
- #ifdef __64BIT__
- suffix.append("64");
- #else
- suffix.append("32");
- #endif
- return suffix;
- }
- typedef MapStringTo<bool> UniqueScopes;
- static void wuAccessError(const char *username, const char *action, const char *wuscope, const char *wuid, bool excpt, bool log)
- {
- StringBuffer err;
- err.append("Workunit Access Denied - action: ").append(action).append(" user:").append(username ? username : "<Unknown>");
- if (wuid)
- err.append(" workunit:").append(wuid);
- if (wuscope)
- err.append(" scope:").append(wuscope);
- //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
- SYSLOG(AUDIT_TYPE_ACCESS_FAILURE, err.str());
- if (log)
- LOG(MCuserError, "%s", err.str());
- if (excpt)
- throw MakeStringException(WUERR_AccessError, "%s", err.str());
- }
- static bool checkWuScopeSecAccess(const char *wuscope, ISecManager *secmgr, ISecUser *secuser, int required, const char *action, bool excpt, bool log)
- {
- if (!secmgr || !secuser)
- return true;
- bool ret = secmgr->authorizeEx(RT_WORKUNIT_SCOPE, *secuser, wuscope)>=required;
- if (!ret && (log || excpt))
- wuAccessError(secuser->getName(), action, wuscope, NULL, excpt, log);
- return ret;
- }
- static bool checkWuScopeListSecAccess(const char *wuscope, ISecResourceList *scopes, int required, const char *action, bool excpt, bool log)
- {
- if (!scopes)
- return true;
- bool ret=true;
- if (wuscope)
- {
- Owned<ISecResource> res=scopes->getResource(wuscope);
- if (!res || res->getAccessFlags()<required)
- ret=false;
- }
- else
- {
- for (int seq=0; ret && seq<scopes->count(); seq++)
- {
- ISecResource *res=scopes->queryResource(seq);
- if (res && res->getAccessFlags()<required)
- return false;
- }
- }
- if (!ret && (log || excpt))
- wuAccessError(NULL, action, wuscope, NULL, excpt, log);
- return ret;
- }
- static bool checkWuSecAccess(IConstWorkUnit &cw, ISecManager *secmgr, ISecUser *secuser, int required, const char *action, bool excpt, bool log)
- {
- if (!secmgr || !secuser)
- return true;
- bool ret=secmgr->authorizeEx(RT_WORKUNIT_SCOPE, *secuser, cw.queryWuScope())>=required;
- if (!ret && (log || excpt))
- {
- wuAccessError(secuser->getName(), action, cw.queryWuScope(), cw.queryWuid(), excpt, log);
- }
- return ret;
- }
- static bool checkWuSecAccess(const char *wuid, ISecManager *secmgr, ISecUser *secuser, int required, const char *action, bool excpt, bool log)
- {
- if (!secmgr || !secuser)
- return true;
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- Owned<IConstWorkUnit> cw = factory->openWorkUnit(wuid);
- bool ret=secmgr->authorizeEx(RT_WORKUNIT_SCOPE, *secuser, cw->queryWuScope())>=required;
- if (!ret && (log || excpt))
- {
- wuAccessError(secuser->getName(), action, cw->queryWuScope(), cw->queryWuid(), excpt, log);
- }
- return ret;
- }
- void doDescheduleWorkkunit(char const * wuid)
- {
- StringBuffer xpath;
- xpath.append("*/*/*/");
- ncnameEscape(wuid, xpath);
- Owned<IRemoteConnection> conn = querySDS().connect("/Schedule", myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if(!conn) return;
- Owned<IPropertyTree> root = conn->getRoot();
- bool more;
- do more = root->removeProp(xpath.str()); while(more);
- }
- //======================================================
- /*
- * Graph progress support
- */
- CWuGraphStats::CWuGraphStats(StatisticCreatorType _creatorType, const char * _creator, unsigned wfid, const char * _rootScope, unsigned _id, bool _merge)
- : creatorType(_creatorType), creator(_creator), id(_id), merge(_merge)
- {
- StatsScopeId graphScopeId;
- verifyex(graphScopeId.setScopeText(_rootScope));
- StatsScopeId rootScopeId(SSTworkflow,wfid);
- collector.setown(createStatisticsGatherer(_creatorType, _creator, rootScopeId));
- collector->beginScope(graphScopeId);
- }
- void CWuGraphStats::beforeDispose()
- {
- collector->endScope();
- StringBuffer tag;
- tag.append("sg").append(id);
- IPropertyTree &progress = queryProgressTree();
- if (merge && progress.hasProp(tag))
- {
- VStringBuffer statsPath("%s/Stats", tag.str());
- MemoryBuffer compressed;
- progress.getPropBin(statsPath, compressed);
- if (compressed.length())
- {
- MemoryBuffer serialized;
- decompressToBuffer(serialized, compressed);
- Owned<IStatisticCollection> prevCollection = createStatisticCollection(serialized);
- prevCollection->mergeInto(*collector);
- }
- }
- Owned<IStatisticCollection> stats = collector->getResult();
- MemoryBuffer compressed;
- {
- MemoryBuffer serialized;
- serializeStatisticCollection(serialized, stats);
- compressToBuffer(compressed, serialized.length(), serialized.toByteArray());
- }
- unsigned minActivity = 0;
- unsigned maxActivity = 0;
- stats->getMinMaxActivity(minActivity, maxActivity);
- //Replace the particular subgraph statistics added by this creator
- IPropertyTree * subgraph = progress.setPropTree(tag);
- subgraph->setProp("@c", queryCreatorTypeName(creatorType));
- subgraph->setProp("@creator", creator);
- subgraph->setPropInt("@minActivity", minActivity);
- subgraph->setPropInt("@maxActivity", maxActivity);
- subgraph->setPropBin("Stats", compressed.length(), compressed.toByteArray());
- if (!progress.getPropBool("@stats", false))
- progress.setPropBool("@stats", true);
- }
- IStatisticGatherer & CWuGraphStats::queryStatsBuilder()
- {
- return *collector;
- }
- class CConstGraphProgress : public CInterface, implements IConstWUGraphProgress
- {
- public:
- IMPLEMENT_IINTERFACE;
- CConstGraphProgress(const char *_wuid, const char *_graphName, IPropertyTree *_progress) : wuid(_wuid), graphName(_graphName), progress(_progress)
- {
- if (!progress)
- progress.setown(createPTree());
- formatVersion = progress->getPropInt("@format", PROGRESS_FORMAT_V);
- }
- virtual IPropertyTree * getProgressTree(bool doFormat) override
- {
- if (progress->getPropBool("@stats"))
- return createProcessTreeFromStats(doFormat); // Should we cache that?
- return LINK(progress);
- }
- virtual unsigned queryFormatVersion()
- {
- return formatVersion;
- }
- protected:
- CConstGraphProgress(const char *_wuid, const char *_graphName) : wuid(_wuid), graphName(_graphName)
- {
- formatVersion = PROGRESS_FORMAT_V;
- }
- static void expandStats(IPropertyTree * target, IStatisticCollection & collection, bool doFormat)
- {
- StringBuffer formattedValue;
- unsigned numStats = collection.getNumStatistics();
- for (unsigned i=0; i < numStats; i++)
- {
- StatisticKind kind;
- unsigned __int64 value;
- collection.getStatistic(kind, value, i);
- if (doFormat)
- {
- formatStatistic(formattedValue.clear(), value, kind);
- target->setProp(queryTreeTag(kind), formattedValue);
- }
- else
- {
- target->setPropInt64(queryTreeTag(kind), value);
- }
- }
- }
- void expandProcessTreeFromStats(IPropertyTree * rootTarget, IPropertyTree * target, IStatisticCollection * collection, bool doFormat)
- {
- expandStats(target, *collection, doFormat);
- StringBuffer scopeName;
- Owned<IStatisticCollectionIterator> activityIter = &collection->getScopes(NULL, false);
- ForEach(*activityIter)
- {
- IStatisticCollection & cur = activityIter->query();
- cur.getScope(scopeName.clear());
- const char * id = scopeName.str();
- const char * tag;
- IPropertyTree * curTarget = target;
- switch (cur.queryScopeType())
- {
- case SSTedge:
- tag = "edge";
- id += strlen(EdgeScopePrefix);
- break;
- case SSTactivity:
- tag = "node";
- id += strlen(ActivityScopePrefix);
- break;
- case SSTsubgraph:
- //All subgraphs are added a root elements in the progress tree
- curTarget = rootTarget;
- tag = "node";
- id += strlen(SubGraphScopePrefix);
- break;
- case SSTchildgraph:
- case SSTworkflow:
- case SSTgraph:
- // SSTworkflow and SSTgraph may be safely ignored. They are not required to produce the statistics.
- expandProcessTreeFromStats(rootTarget, target, &cur, doFormat);
- continue;
- case SSTfunction:
- case SSTchannel:
- case SSTglobal:
- //MORE:Should function scopes be included in the graph scope somehow, and if so how?
- continue;
- default:
- throwUnexpected();
- }
- IPropertyTree * next = curTarget->addPropTree(tag);
- next->setProp("@id", id);
- expandProcessTreeFromStats(rootTarget, next, &cur, doFormat);
- }
- }
- IPropertyTree * createProcessTreeFromStats(bool doFormat)
- {
- MemoryBuffer compressed;
- MemoryBuffer serialized;
- Owned<IPropertyTree> progressTree = createPTree();
- Owned<IPropertyTreeIterator> iter = progress->getElements("sg*");
- ForEach(*iter)
- {
- IPropertyTree & curSubGraph = iter->query();
- curSubGraph.getPropBin("Stats", compressed.clear());
- //Protect against updates that delete the stats while we are iterating
- if (compressed.length())
- {
- decompressToBuffer(serialized.clear(), compressed);
- Owned<IStatisticCollection> collection = createStatisticCollection(serialized);
- expandProcessTreeFromStats(progressTree, progressTree, collection, doFormat);
- }
- }
- return progressTree.getClear();
- }
- protected:
- Linked<IPropertyTree> progress;
- StringAttr wuid, graphName;
- unsigned formatVersion;
- };
- extern WORKUNIT_API IConstWUGraphProgress *createConstGraphProgress(const char *_wuid, const char *_graphName, IPropertyTree *_progress)
- {
- return new CConstGraphProgress(_wuid, _graphName, _progress);
- }
- //--------------------------------------------------------------------------------------------------------------------
- /*
- * Create a user friendly description a scope/stats combination. Only currently used for elapsed time for root subgraphs
- */
- static void createDefaultDescription(StringBuffer & description, StatisticKind kind, StatisticScopeType scopeType, const char * scope)
- {
- switch (kind)
- {
- case StTimeElapsed:
- {
- if (scopeType != SSTsubgraph)
- break;
- //Create a default description for a root subgraph
- const char * colon = strchr(scope, ':');
- if (!colon)
- break;
- const char * subgraph = colon+1;
- //Check for nested subgraph
- if (strchr(subgraph, ':'))
- break;
- assertex(strncmp(subgraph, SubGraphScopePrefix, strlen(SubGraphScopePrefix)) == 0);
- StringAttr graphname;
- graphname.set(scope, colon - scope);
- unsigned subId = atoi(subgraph + strlen(SubGraphScopePrefix));
- formatGraphTimerLabel(description, graphname, 0, subId);
- return;
- }
- }
- describeScope(description, scope);
- }
- /* Represents a single statistic */
- class ExtractedStatistic : public CInterfaceOf<IConstWUStatistic>
- {
- public:
- virtual IStringVal & getDescription(IStringVal & str, bool createDefault) const
- {
- if (!description && createDefault)
- {
- StringBuffer desc;
- createDefaultDescription(desc, kind, scopeType, scope);
- str.set(desc);
- return str;
- }
- str.set(description);
- return str;
- }
- virtual IStringVal & getCreator(IStringVal & str) const
- {
- str.set(creator);
- return str;
- }
- virtual const char * queryScope() const
- {
- return scope ? scope : "";
- }
- virtual IStringVal & getFormattedValue(IStringVal & str) const
- {
- StringBuffer formatted;
- formatStatistic(formatted, value, measure);
- str.set(formatted);
- return str;
- }
- virtual StatisticMeasure getMeasure() const
- {
- return measure;
- }
- virtual StatisticKind getKind() const
- {
- return kind;
- }
- virtual StatisticCreatorType getCreatorType() const
- {
- return creatorType;
- }
- virtual StatisticScopeType getScopeType() const
- {
- return scopeType;
- }
- virtual unsigned __int64 getValue() const
- {
- return value;
- }
- virtual unsigned __int64 getCount() const
- {
- return count;
- }
- virtual unsigned __int64 getMax() const
- {
- return max;
- }
- virtual unsigned __int64 getTimestamp() const
- {
- return timeStamp;
- }
- public:
- StringBuffer creator;
- StringBuffer description;
- StringBuffer scope;
- StatisticMeasure measure;
- StatisticKind kind;
- StatisticCreatorType creatorType;
- StatisticScopeType scopeType;
- unsigned __int64 value;
- unsigned __int64 count;
- unsigned __int64 max;
- unsigned __int64 timeStamp;
- };
- //---------------------------------------------------------------------------------------------------------------------
- /*
- * The following compare functions are used to ensure that comparison are consistent with
- * compareScopeName() in jstats. This ensures that the scope iterators from different sources
- * are processed in a consistent order
- */
- static int compareGraphNode(IInterface * const *ll, IInterface * const *rr)
- {
- IPropertyTree *l = (IPropertyTree *) *ll;
- IPropertyTree *r = (IPropertyTree *) *rr;
- unsigned lwfid = l->getPropInt("@wfid");
- unsigned rwfid = r->getPropInt("@wfid");
- if (lwfid != rwfid)
- return lwfid > rwfid ? +1 : -1;
- const char * lname = l->queryName();
- const char * rname = r->queryName();
- return compareScopeName(lname, rname);
- }
- static int compareSubGraphStatsNode(IInterface * const *ll, IInterface * const *rr)
- {
- IPropertyTree *l = (IPropertyTree *) *ll;
- IPropertyTree *r = (IPropertyTree *) *rr;
- return compareScopeName(l->queryName(), r->queryName());
- }
- static int compareSubGraphNode(IInterface * const *ll, IInterface * const *rr)
- {
- IPropertyTree *l = (IPropertyTree *) *ll;
- IPropertyTree *r = (IPropertyTree *) *rr;
- return l->getPropInt("@id") - r->getPropInt("@id");
- }
- static int compareActivityNode(IInterface * const *ll, IInterface * const *rr)
- {
- IPropertyTree *l = (IPropertyTree *) *ll;
- IPropertyTree *r = (IPropertyTree *) *rr;
- return l->getPropInt("@id") - r->getPropInt("@id");
- }
- static int compareEdgeNode(IInterface * const *ll, IInterface * const *rr)
- {
- IPropertyTree *l = (IPropertyTree *) *ll;
- IPropertyTree *r = (IPropertyTree *) *rr;
- //MORE: Edge needs more work
- const char * leftId = l->queryProp("@id");
- const char * rightId = r->queryProp("@id");
- unsigned leftAc = atoi(leftId);
- unsigned rightAc = atoi(rightId);
- if (leftAc != rightAc)
- return (int)(leftAc - rightAc);
- const char * leftSep = strchr(leftId, '_');
- const char * rightSep = strchr(rightId, '_');
- assertex(leftSep && rightSep);
- return atoi(leftSep+1) - atoi(rightSep+1);
- }
- //---------------------------------------------------------------------------------------------------------------------
- /*
- * A class for implementing a scope iterator that walks through graph progress information
- */
- class CConstGraphProgressScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
- {
- public:
- CConstGraphProgressScopeIterator(const char * wuid, const ScopeFilter & _filter, __uint64 _minVersion) : filter(_filter), minVersion(_minVersion)
- {
- //Examine the filter, and determine if we only need to look at a single graph/subgraph
- StringAttr singleGraph;
- const StringArray & scopesToMatch = filter.queryScopes();
- if (scopesToMatch)
- {
- bool seenGraph = false;
- bool seenSubGraph = false;
- ForEachItemIn(iScope, scopesToMatch)
- {
- StringArray ids;
- ids.appendList(scopesToMatch.item(iScope), ":");
- ForEachItemIn(i, ids)
- {
- const char * curId = ids.item(i);
- StatsScopeId id(curId);
- switch (id.queryScopeType())
- {
- case SSTgraph:
- if (seenGraph)
- {
- if (singleGraph && !streq(singleGraph, curId))
- singleGraph.clear();
- }
- else
- {
- if (!id.isWildcard())
- singleGraph.set(curId);
- seenGraph = true;
- }
- break;
- case SSTsubgraph:
- if (seenSubGraph)
- {
- if (singleSubGraph && !streq(singleSubGraph, curId))
- singleSubGraph.clear();
- }
- else
- {
- if (!id.isWildcard())
- singleSubGraph.set(curId);
- seenSubGraph = true;
- }
- break;
- }
- }
- }
- }
- rootPath.append("/GraphProgress/").append(wuid).append('/');
- if (singleGraph)
- rootPath.append(singleGraph).append("/");
- //Don't lock the statistics while we iterate - any partial updates must not cause problems
- if (daliClientActive())
- conn.setown(querySDS().connect(rootPath.str(), myProcessSession(), RTM_NONE, SDS_LOCK_TIMEOUT));
- if (conn && !singleGraph)
- {
- graphIter.setown(conn->queryRoot()->getElements("*"));
- graphIter.setown(createSortedIterator(*graphIter, compareGraphNode));
- }
- valid = false;
- }
- IMPLEMENT_IINTERFACE_USING(CInterfaceOf<IConstWUScopeIterator>)
- virtual bool first()
- {
- valid = false;
- if (!conn)
- return false;
- if (graphIter && !graphIter->first())
- return false;
- if (!firstSubGraph())
- {
- if (!nextGraph())
- return false;
- }
- valid = true;
- return true;
- }
- virtual bool next()
- {
- if (!nextChildScope())
- {
- if (!nextSubGraph())
- {
- if (!nextGraph())
- {
- valid = false;
- return false;
- }
- }
- }
- return true;
- }
- virtual bool nextSibling() override
- {
- if (collections.ordinality() == 0)
- return false;
- assertex(childIterators.ordinality() < collections.ordinality());
- collections.pop();
- //next will call childIterator.next() - walking the next sibling
- return next();
- }
- virtual bool nextParent() override
- {
- if (collections.ordinality() == 0)
- return false;
- assertex(childIterators.ordinality() < collections.ordinality());
- collections.pop();
- if (collections.ordinality() == 0)
- return false;
- //Finish with this node - so next will move onto the sibling of the parent node.
- finishCollection();
- return next();
- }
- virtual bool isValid()
- {
- return valid;
- }
- protected:
- bool firstSubGraph()
- {
- IPropertyTree & graphNode = graphIter ? graphIter->query() : *conn->queryRoot();
- const char * xpath = "sg*";
- StringBuffer childXpath;
- if (singleSubGraph)
- {
- childXpath.append(singleSubGraph);
- xpath = childXpath.str();
- }
- subgraphIter.setown(graphNode.getElements(xpath));
- if (subgraphIter)
- {
- if (!singleSubGraph)
- subgraphIter.setown(createSortedIterator(*subgraphIter, compareSubGraphStatsNode));
- }
- else
- subgraphIter.setown(graphNode.getElements("sg0"));
- if (!subgraphIter->first())
- return false;
- if (firstStat())
- return true;
- return nextSubGraph();
- }
- bool nextSubGraph()
- {
- for (;;)
- {
- if (!subgraphIter->next())
- return false;
- if (firstStat())
- return true;
- }
- }
- bool nextGraph()
- {
- if (!graphIter)
- return false;
- for (;;)
- {
- if (!graphIter->next())
- return false;
- if (firstSubGraph())
- return true;
- }
- }
- bool firstStat()
- {
- IPropertyTree & curSubGraph = subgraphIter->query();
- if (!checkSubGraph())
- return false;
- curSubGraph.getPropBin("Stats", compressed.clear());
- //Don't crash on old format progress...
- if (compressed.length() == 0)
- return false;
- decompressToBuffer(serialized.clear(), compressed);
- Owned<IStatisticCollection> collection = createStatisticCollection(serialized);
- statsIterator.timeStamp = collection->queryWhenCreated();
- if (!beginCollection(*collection))
- return false;
- // When workflow is root element, it is just a container. Ignore the workflow element here
- // as WorkUnitStatisticsScopeIterator will produce workflow scope - don't want duplicates.
- // (Note: workflow element never contains stats).
- if (collections.tos().queryScopeType() == SSTworkflow)
- {
- if (!next())
- return false;
- }
- //The root element of a collection is a graph - but it is only there to nest the subgraphs in.
- //Do not iterate it as a separate element - unless it has some stats.
- IStatisticCollection & curCollection = collections.tos();
- if ((curCollection.queryScopeType() != SSTgraph) || (curCollection.getNumStatistics() != 0))
- return true;
- return next();
- }
- bool beginCollection(IStatisticCollection & collection)
- {
- collections.append(OLINK(collection));
- curScopeType = collection.queryScopeType();
- collection.getFullScope(curScopeName.clear());
- ScopeCompare result = filter.compare(curScopeName);
- if (result & SCequal)
- return true;
- //If this scope cannot be the parent of a match then discard it.
- if (!(result & SCparent))
- collections.pop();
- //walk the next element
- return nextChildScope();
- }
- bool nextChildScope()
- {
- for (;;)
- {
- if (collections.ordinality() == 0)
- return false;
- IStatisticCollection * curCollection = &collections.tos();
- if (childIterators.ordinality() < collections.ordinality())
- {
- ScopeCompare result = filter.compare(curScopeName);
- //Do not walk children scopes if it is unrelated
- if (result & SCparent)
- {
- //Start iterating the children for the current collection
- childIterators.append(curCollection->getScopes(NULL, true));
- if (!childIterators.tos().first())
- {
- finishCollection();
- continue;
- }
- }
- else
- {
- //Don't walk the child scopes
- collections.pop();
- continue;
- }
- }
- else if (!childIterators.tos().next())
- {
- finishCollection();
- continue;
- }
- if (beginCollection(childIterators.tos().query()))
- return true;
- }
- }
- void finishCollection()
- {
- collections.pop();
- childIterators.pop();
- }
- bool checkSubGraph()
- {
- IPropertyTree & curSubGraph = subgraphIter->query();
- StatisticCreatorType creatorType = queryCreatorType(curSubGraph.queryProp("@c"), SCTnone);
- const char * creator = curSubGraph.queryProp("@creator");
- //MORE: Check minVersion and allow early filtering
- //MORE: Potentially filter by creator type??
- // if (!filter->matches(creatorType, creator, SSTall, NULL, SMeasureAll, StKindAll, AnyStatisticValue))
- // return false;
- statsIterator.creatorType = creatorType;
- statsIterator.creator.set(creator);
- return true;
- }
- virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
- {
- if ((whichProperties & PTstatistics))
- {
- /*
- MORE: Code from the statsIterator class should be inlined here - code will be much simpler
- but it currently needs an implementation of the IConstWUStatistic 3rd parameter
- IStatisticCollection & collection = collections.tos();
- ForEachItemIn(i, collection)
- {
- StatisticKind kind;
- unsigned __int64 value;
- collection->getStatistic(kind, value, i);
- visitor.noteStatistic(kind, value, *this);
- }
- */
- statsIterator.reset(curScopeName, curScopeType, collections.tos());
- ForEach(statsIterator)
- statsIterator.play(visitor);
- }
- }
- virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
- {
- return collections.tos().getStatistic(kind, value);
- }
- virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
- {
- return nullptr;
- }
- virtual const char * queryHint(const char * kind) const override
- {
- return nullptr;
- }
- virtual const char * queryScope() const override
- {
- return curScopeName;
- }
- virtual StatisticScopeType getScopeType() const override
- {
- return curScopeType;
- }
- private:
- class ScopeStatisticsIterator : public CInterfaceOf<IConstWUStatistic>
- {
- friend class CConstGraphProgressScopeIterator; // cleaner if this was removed + setScope() functions added.
- public:
- void reset(const char * _scopeName, StatisticScopeType _scopeType, IStatisticCollection & _collection)
- {
- scope.set(_scopeName);
- scopeType = _scopeType;
- collection = &_collection;
- numStats = collection->getNumStatistics();
- }
- // interface IConstWUStatisticIterator
- IConstWUStatistic & query()
- {
- return *this;
- }
- bool first()
- {
- curStatIndex = 0;
- if (curStatIndex >= numStats)
- return false;
- collection->getStatistic(kind, value, curStatIndex);
- //MORE: Allow filtering:
- /*
- * if (filter && !filter->matches(SCTall, NULL, SSTall, NULL, queryMeasure(kind), kind, value))
- continue;
- */
- return true;
- }
- bool next()
- {
- for (;;)
- {
- ++curStatIndex;
- if (curStatIndex >= numStats)
- return false;
- collection->getStatistic(kind, value, curStatIndex);
- //MORE: Allow stats filtering
- return true;
- }
- }
- bool isValid()
- {
- return curStatIndex < numStats;
- }
- //interface IConstWUStatistic
- virtual IStringVal & getDescription(IStringVal & str, bool createDefault) const override
- {
- if (createDefault)
- {
- StringBuffer description;
- createDefaultDescription(description, kind, scopeType, scope);
- str.set(description);
- }
- return str;
- }
- virtual IStringVal & getCreator(IStringVal & str) const override
- {
- str.set(creator);
- return str;
- }
- virtual const char * queryScope() const override
- {
- return scope;
- }
- virtual IStringVal & getFormattedValue(IStringVal & str) const override
- {
- StringBuffer formatted;
- formatStatistic(formatted, value, kind);
- str.set(formatted);
- return str;
- }
- virtual StatisticMeasure getMeasure() const override
- {
- return queryMeasure(kind);
- }
- virtual StatisticKind getKind() const override
- {
- return kind;
- }
- virtual StatisticCreatorType getCreatorType() const override
- {
- return creatorType;
- }
- virtual StatisticScopeType getScopeType() const override
- {
- return scopeType;
- }
- virtual unsigned __int64 getValue() const override
- {
- return value;
- }
- virtual unsigned __int64 getCount() const override
- {
- return 1;
- }
- virtual unsigned __int64 getMax() const override
- {
- return 0;
- }
- virtual unsigned __int64 getTimestamp() const override
- {
- return timeStamp;
- }
- void play(IWuScopeVisitor & visitor)
- {
- visitor.noteStatistic(kind, value, *this);
- }
- protected:
- IStatisticCollection * collection = nullptr;
- StringBuffer creator;
- StringBuffer scope;
- StatisticKind kind;
- StatisticCreatorType creatorType;
- StatisticScopeType scopeType;
- unsigned __int64 value = 0;
- unsigned __int64 timeStamp;
- unsigned curStatIndex = 0;
- unsigned numStats = 0;
- } statsIterator;
- Owned<IRemoteConnection> conn;
- StringBuffer curScopeName;
- StatisticScopeType curScopeType = SSTnone;
- __uint64 minVersion;
- const ScopeFilter & filter;
- StringBuffer rootPath;
- StringAttr singleSubGraph;
- Owned<IPropertyTreeIterator> graphIter;
- Owned<IPropertyTreeIterator> subgraphIter;
- IArrayOf<IStatisticCollection> collections;
- IArrayOf<IStatisticCollectionIterator> childIterators; // Iterator(n) through collections(n) - created once iterating children
- MemoryBuffer compressed;
- MemoryBuffer serialized;
- bool valid;
- };
- int compareNoteScopeOrder(IConstWUException & left, IConstWUException & right)
- {
- return compareScopeName(left.queryScope(), right.queryScope());
- }
- int compareNoteScopeOrder(IInterface * const * left, IInterface * const * right)
- {
- return compareNoteScopeOrder(*static_cast<IConstWUException *>(*left), *static_cast<IConstWUException *>(*right));
- }
- class NotesIterator : public CInterfaceOf<IConstWUScopeIterator>
- {
- public:
- NotesIterator(const IConstWorkUnit * wu, const ScopeFilter & _filter)
- {
- Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
- ForEach(*exceptions)
- {
- IConstWUException & exception = exceptions->query();
- if (exception.queryScope()!=nullptr)
- notes.append(OLINK(exception));
- }
- notes.sort(compareNoteScopeOrder);
- }
- virtual bool first() override
- {
- baseIndex = 0;
- return updateCurrent();
- }
- virtual bool next() override
- {
- baseIndex += numCurrentScope;
- return updateCurrent();
- }
- virtual bool isValid() override
- {
- return notes.isItem(baseIndex);
- }
- virtual bool nextSibling() override
- {
- //Search until the current scope is not a child of the previous scope
- StringBuffer savedScope(queryScope());
- for (;;)
- {
- if (!next())
- return false;
- if (compareScopes(queryScope(), savedScope) != SCchild)
- return true;
- }
- }
- virtual bool nextParent() override
- {
- //Search until the current scope is not a child of the previous parent scope
- StringBuffer parentScope;
- if (getParentScope(parentScope, queryScope()))
- {
- for (;;)
- {
- if (!next())
- return false;
- if (compareScopes(queryScope(), parentScope) != SCchild)
- return true;
- }
- }
- else
- {
- finish();
- return false;
- }
- }
- virtual const char * queryScope() const override
- {
- const char * scope = notes.item(baseIndex).queryScope();
- return scope ? scope : "";
- }
- virtual StatisticScopeType getScopeType() const override
- {
- const char * tail = queryScopeTail(queryScope());
- StatsScopeId id(tail);
- return id.queryScopeType();
- }
- virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties = PTall) override
- {
- if (whichProperties & PTnotes)
- {
- for (unsigned i=0; i < numCurrentScope; i++)
- {
- IConstWUException & cur = notes.item(baseIndex + i);
- visitor.noteException(cur);
- }
- }
- }
- virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
- {
- return false;
- }
- virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
- {
- return nullptr;
- }
- virtual const char * queryHint(const char * kind) const override
- {
- return nullptr;
- }
- private:
- bool updateCurrent()
- {
- if (!notes.isItem(baseIndex))
- return false;
- // set numCurrentScope to number of notes in the current scope
- unsigned next = baseIndex+1;
- while (next < notes.ordinality())
- {
- if (compareNoteScopeOrder(notes.item(baseIndex), notes.item(next)) != 0)
- break;
- next++;
- }
- numCurrentScope = (next - baseIndex);
- return true;
- }
- void finish()
- {
- baseIndex = notes.ordinality();
- }
- unsigned baseIndex = 0;
- unsigned numCurrentScope = 0;
- IArrayOf<IConstWUException> notes;
- };
- int compareStatisticScopes(IConstWUStatistic & left, IConstWUStatistic & right)
- {
- return compareScopeName(left.queryScope(), right.queryScope());
- }
- int compareStatisticScopes(IInterface * const * left, IInterface * const * right)
- {
- return compareStatisticScopes(*static_cast<IConstWUStatistic *>(*left), *static_cast<IConstWUStatistic *>(*right));
- }
- /*
- * An implementation of IConstWUScopeIterator for global workunit statistics.
- */
- class WorkUnitStatisticsScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
- {
- public:
- WorkUnitStatisticsScopeIterator(const IArrayOf<IConstWUStatistic> & _statistics, const ScopeFilter & _filter)
- {
- ForEachItemIn(i, _statistics)
- {
- IConstWUStatistic & cur = _statistics.item(i);
- if (_filter.compare(cur.queryScope()) & SCequal)
- statistics.append(OLINK(cur));
- }
- statistics.sort(compareStatisticScopes);
- }
- virtual bool first() override
- {
- curIndex = 0;
- return initScope();
- }
- virtual bool next() override
- {
- curIndex += numStatistics;
- return initScope();
- }
- virtual bool nextSibling() override
- {
- //Search until the current scope is not a child of the previous scope
- StringBuffer savedScope(queryScope());
- for (;;)
- {
- if (!next())
- return false;
- if (compareScopes(queryScope(), savedScope) != SCchild)
- return true;
- }
- }
- virtual bool nextParent() override
- {
- //Search until the current scope is not a child of the previous parent scope
- StringBuffer parentScope;
- if (getParentScope(parentScope, queryScope()))
- {
- for (;;)
- {
- if (!next())
- return false;
- if (compareScopes(queryScope(), parentScope) != SCchild)
- return true;
- }
- }
- else
- {
- finish();
- return false;
- }
- }
- virtual bool isValid() override
- {
- return statistics.isItem(curIndex);
- }
- virtual const char * queryScope() const override
- {
- return statistics.item(curIndex).queryScope();
- }
- virtual StatisticScopeType getScopeType() const override
- {
- return statistics.item(curIndex).getScopeType();
- }
- virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
- {
- if (whichProperties & PTstatistics)
- {
- for (unsigned i=0; i < numStatistics; i++)
- {
- IConstWUStatistic & cur = statistics.item(curIndex + i);
- visitor.noteStatistic(cur.getKind(), cur.getValue(), cur);
- }
- }
- }
- virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
- {
- for (unsigned i=0; i < numStatistics; i++)
- {
- IConstWUStatistic & cur = statistics.item(curIndex + i);
- if (cur.getKind() == kind)
- {
- value = cur.getValue();
- return true;
- }
- }
- return false;
- }
- virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
- {
- return nullptr;
- }
- virtual const char * queryHint(const char * kind) const
- {
- return nullptr;
- }
- protected:
- inline IConstWUStatistic & queryStatistic(unsigned i)
- {
- return statistics.item(curIndex + i);
- }
- bool initScope()
- {
- if (!statistics.isItem(curIndex))
- return false;
- unsigned next = curIndex+1;
- while (next < statistics.ordinality())
- {
- if (compareStatisticScopes(statistics.item(curIndex), statistics.item(next)) != 0)
- break;
- next++;
- }
- numStatistics = (next - curIndex);
- return true;
- }
- void finish()
- {
- curIndex = statistics.ordinality();
- }
- protected:
- IArrayOf<IConstWUStatistic> statistics;
- unsigned curIndex = 0;
- unsigned numStatistics = 1;
- };
- /*
- * An implementation of IConstWUScopeIterator for the query graphs.
- */
- class GraphScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
- {
- private:
- //This uses a state machine - this enumeration contains the different states.
- enum State
- {
- //Following are the states which represent valid scopes
- SGraph,
- SChildGraph,
- SSubGraph,
- SEdge,
- SActivity,
- //The following are internal states.
- SGraphFirstEdge,
- SGraphFirstSubGraph,
- SGraphFirst,
- SGraphEnd,
- SGraphNext,
- SSubGraphFirstEdge,
- SSubGraphFirstActivity,
- SSubGraphEnd,
- SSubGraphNext,
- SEdgeNext,
- SEdgeEnd,
- SActivityNext,
- SActivityEnd,
- SChildGraphFirstEdge,
- SChildGraphFirstSubGraph,
- SChildGraphFirst,
- SChildGraphNext,
- SChildGraphEnd,
- SDone
- };
- State state = SDone;
- State nextState = SDone;
- public:
- GraphScopeIterator(const IConstWorkUnit * wu, const ScopeFilter & _filter) : graphIter(&wu->getGraphs(GraphTypeAny)), filter(_filter)
- {
- }
- virtual bool first() override
- {
- state = SGraphFirst;
- return nextScope();
- }
- virtual bool next() override
- {
- if (!selectNext())
- return false;
- return nextScope();
- }
- virtual bool nextSibling() override
- {
- if (!selectNextSibling())
- return false;
- return nextScope();
- }
- virtual bool nextParent() override
- {
- if (!selectNextParent())
- return false;
- return nextScope();
- }
- virtual bool isValid() override
- {
- return graphIter->isValid();
- }
- virtual const char * queryScope() const override
- {
- return curScopeName.str();
- }
- virtual StatisticScopeType getScopeType() const override
- {
- return scopeType;
- }
- virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
- {
- switch (scopeType)
- {
- case SSTgraph:
- return;
- }
- IPropertyTree & cur = treeIters.tos().query();
- switch (scopeType)
- {
- case SSTgraph:
- break;
- case SSTsubgraph:
- break;
- case SSTactivity:
- {
- if (whichProperties & PTattributes)
- {
- playAttribute(visitor, WaLabel);
- Owned<IPropertyTreeIterator> attrs = cur.getElements("att");
- ForEach(*attrs)
- {
- IPropertyTree & cur = attrs->query();
- WuAttr attr = queryGraphChildAttToWuAttr(cur.queryProp("@name"));
- if (attr != WaNone)
- visitor.noteAttribute(attr, cur.queryProp("@value"));
- }
- }
- if (whichProperties & PThints)
- {
- Owned<IPropertyTreeIterator> hints = cur.getElements("hint");
- ForEach(*hints)
- {
- IPropertyTree & cur = hints->query();
- visitor.noteHint(cur.queryProp("@name"), cur.queryProp("@value"));
- }
- }
- break;
- }
- case SSTedge:
- if (whichProperties & PTattributes)
- {
- //MORE This will eventually need to walk the attributes and map the names.
- //Need to be careful if they need to be mapped differently depending on the context.
- playAttribute(visitor, WaLabel);
- playAttribute(visitor, WaIdSource);
- playAttribute(visitor, WaIdTarget);
- playAttribute(visitor, WaSourceIndex);
- playAttribute(visitor, WaTargetIndex);
- playAttribute(visitor, WaIsDependency);
- }
- break;
- }
- }
- virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
- {
- return false;
- }
- virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
- {
- if (!treeIters.ordinality())
- return nullptr;
- //MORE - check that the attribute is value for the current scope type (to prevent defaults being returned)
- return queryAttributeValue(treeIters.tos().query(), attr, scratchpad);
- }
- virtual const char * queryHint(const char * kind) const override
- {
- //MORE: Needs to be implemented!
- return nullptr;
- }
- private:
- void playAttribute(IWuScopeVisitor & visitor, WuAttr kind)
- {
- StringBuffer scratchpad;
- const char * value = queryAttributeValue(treeIters.tos().query(), kind, scratchpad);
- if (value)
- visitor.noteAttribute(kind, value);
- }
- void pushIterator(IPropertyTreeIterator * iter, State state)
- {
- treeIters.append(*LINK(iter));
- stateStack.append(state);
- }
- State popIterator()
- {
- treeIters.pop();
- return (State)stateStack.popGet();
- }
- void pushScope(const char * id)
- {
- scopeLengths.append(curScopeName.length());
- curScopeName.append(":").append(id);
- }
- void popScope()
- {
- curScopeName.setLength(scopeLengths.popGet());
- }
- bool doNextScope()
- {
- for(;;)
- {
- switch (state)
- {
- case SGraph:
- {
- IConstWUGraph & graph = graphIter->query();
- unsigned wfid = graph.getWfid();
- curScopeName.clear();
- if (wfid != 0)
- curScopeName.append(WorkflowScopePrefix).append(wfid).append(':');
- graph.getName(StringBufferAdaptor(curScopeName));
- scopeType = SSTgraph;
- return true;
- }
- case SSubGraph:
- scopeId.set(SubGraphScopePrefix).append(treeIters.tos().query().getPropInt("@id"));
- pushScope(scopeId);
- scopeType = SSTsubgraph;
- return true;
- case SEdge:
- scopeId.set(EdgeScopePrefix).append(treeIters.tos().query().queryProp("@id"));
- pushScope(scopeId);
- scopeType = SSTedge;
- return true;
- case SActivity:
- if (treeIters.tos().query().getPropInt("att[@name='_kind']/@value") == TAKsubgraph)
- {
- state = SActivityNext;
- break;
- }
- scopeId.set(ActivityScopePrefix).append(treeIters.tos().query().getPropInt("@id"));
- pushScope(scopeId);
- scopeType = SSTactivity;
- return true;
- case SChildGraph:
- {
- #ifdef _DEBUG
- assertex(treeIters.tos().query().getPropInt("att[@name='_kind']/@value") == TAKsubgraph);
- unsigned numIters = treeIters.ordinality();
- unsigned parentActivityId = treeIters.item(numIters-2).query().getPropInt("@id");
- unsigned parentId = treeIters.tos().query().getPropInt("att[@name='_parentActivity']/@value");
- assertex(parentId == parentActivityId);
- #endif
- scopeId.set(ChildGraphScopePrefix).append(treeIters.tos().query().getPropInt("@id"));
- pushScope(scopeId);
- scopeType = SSTchildgraph;
- return true;
- }
- //Graph iteration
- case SGraphFirst:
- if (!graphIter->first())
- state = SDone;
- else
- state = SGraph;
- break;
- case SGraphEnd:
- state = SGraphNext;
- break;
- case SGraphNext:
- if (!graphIter->next())
- state = SDone;
- else
- state = SGraph;
- break;
- //Edge iteration
- case SGraphFirstEdge:
- {
- //Walk dependencies - should possibly have a different SST e.g., SSTdependency since they do not
- //share many characteristics with edges - e.g. no flowing records => few/no stats.
- curGraph.setown(graphIter->query().getXGMMLTree(false, false));
- Owned<IPropertyTreeIterator> treeIter = curGraph->getElements("edge");
- if (treeIter && treeIter->first())
- {
- treeIter.setown(createSortedIterator(*treeIter, compareEdgeNode));
- treeIter->first();
- pushIterator(treeIter, SGraphFirstSubGraph);
- state = SEdge;
- }
- else
- state = SGraphFirstSubGraph;
- break;
- }
- case SChildGraphFirstEdge:
- {
- Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/edge");
- if (treeIter && treeIter->first())
- {
- treeIter.setown(createSortedIterator(*treeIter, compareEdgeNode));
- pushIterator(treeIter, SChildGraphFirstSubGraph);
- state = SEdge;
- }
- else
- state = SChildGraphFirstSubGraph;
- break;
- }
- case SEdgeEnd:
- popScope();
- state = SEdgeNext;
- break;
- case SEdgeNext:
- if (treeIters.tos().next())
- state = SEdge;
- else
- state = popIterator();
- break;
- //Subgraph iteration
- case SGraphFirstSubGraph:
- {
- Owned<IPropertyTreeIterator> treeIter = curGraph->getElements("node");
- if (treeIter && treeIter->first())
- {
- treeIter.setown(createSortedIterator(*treeIter, compareSubGraphNode));
- treeIter->first();
- pushIterator(treeIter, SGraphNext);
- state = SSubGraph;
- }
- else
- state = SGraphNext;
- break;
- }
- case SChildGraphFirstSubGraph:
- {
- Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/node");
- if (treeIter && treeIter->first())
- {
- treeIter.setown(createSortedIterator(*treeIter, compareSubGraphNode));
- pushIterator(treeIter, SChildGraphEnd);
- state = SSubGraph;
- }
- else
- state = SChildGraphEnd;
- break;
- }
- case SSubGraphFirstEdge:
- {
- Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/edge");
- if (treeIter && treeIter->first())
- {
- treeIter.setown(createSortedIterator(*treeIter, compareEdgeNode));
- pushIterator(treeIter, SSubGraphFirstActivity);
- state = SEdge;
- }
- else
- state = SSubGraphFirstActivity;
- break;
- }
- case SSubGraphFirstActivity:
- {
- //Walk the contents of each subgraph once, splitting the entries into activities and child graphs
- IArrayOf<IPropertyTree> activities;
- IArrayOf<IPropertyTree> childGraphs;
- Owned<IPropertyTreeIterator> treeIter = treeIters.tos().query().getElements("att/graph/node");
- ForEach(*treeIter)
- {
- IPropertyTree & cur = treeIter->get();
- if (cur.getPropInt("att[@name='_kind']/@value") != TAKsubgraph)
- activities.append(cur);
- else
- childGraphs.append(cur);
- }
- if (activities.ordinality())
- {
- //Create an iterator for the child graphs in this subgraph which is iterated in order as the activities are processed
- Owned<IPropertyTreeIterator> graphIter = createSortedIterator(childGraphs, compareSubGraphNode);
- graphIter->first();
- childGraphIters.append(*graphIter.getClear());
- Owned<IPropertyTreeIterator> activityIter = createSortedIterator(activities, compareActivityNode);
- pushIterator(activityIter, SSubGraphEnd);
- state = SActivity;
- }
- else
- state = SSubGraphEnd;
- break;
- }
- case SSubGraphEnd:
- popScope();
- state = SSubGraphNext;
- break;
- case SChildGraphEnd:
- popScope();
- state = SChildGraphNext;
- break;
- case SSubGraphNext:
- if (treeIters.tos().next())
- state = SSubGraph;
- else
- state = popIterator();
- break;
- case SActivityEnd:
- popScope();
- state = SActivityNext;
- break;
- case SActivityNext:
- if (treeIters.tos().next())
- state = SActivity;
- else
- {
- assertex(!childGraphIters.tos().isValid());
- childGraphIters.pop();
- state = popIterator();
- }
- break;
- case SChildGraphNext:
- if (treeIters.tos().next())
- state = SChildGraph;
- else
- state = popIterator();
- break;
- case SChildGraphFirst:
- {
- unsigned parentActivityId = treeIters.tos().query().getPropInt("@id");
- IArrayOf<IPropertyTree> childGraphs;
- IPropertyTreeIterator & allGraphs = childGraphIters.tos();
- while (allGraphs.isValid())
- {
- IPropertyTree & cur = allGraphs.query();
- unsigned parentId = cur.getPropInt("att[@name='_parentActivity']/@value");
- if (parentId != parentActivityId)
- break;
- childGraphs.append(OLINK(cur));
- allGraphs.next();
- }
- if (childGraphs.ordinality())
- {
- Owned<IPropertyTreeIterator> treeIter = createSortedIterator(childGraphs, compareSubGraphNode);
- pushIterator(treeIter, SActivityEnd);
- state = SChildGraph;
- }
- else
- state = SActivityEnd;
- break;
- }
- case SDone:
- return false;
- default:
- throwUnexpected();
- }
- }
- }
- bool nextScope()
- {
- for(;;)
- {
- if (!doNextScope())
- return false;
- ScopeCompare cmp = filter.compare(curScopeName);
- if (cmp & SCequal)
- return true;
- //MORE: Optimize next based on result of compare
- if (!selectNext())
- return false;
- }
- }
- bool selectNext()
- {
- switch (state)
- {
- case SGraph:
- state = SGraphFirstEdge;
- break;
- case SChildGraph:
- state = SChildGraphFirstEdge;
- break;
- case SSubGraph:
- state = SSubGraphFirstEdge;
- break;
- case SEdge:
- state = SEdgeEnd;
- break;
- case SActivity:
- state = SChildGraphFirst;
- break;
- case SDone:
- return false;
- default:
- throwUnexpected();
- }
- return true;
- }
- bool selectNextSibling()
- {
- switch (state)
- {
- case SGraph:
- state = SGraphEnd;
- break;
- case SChildGraph:
- state = SChildGraphEnd;
- break;
- case SSubGraph:
- state = SSubGraphEnd;
- break;
- case SEdge:
- state = SEdgeEnd;
- break;
- case SActivity:
- state = SActivityEnd;
- break;
- case SDone:
- return false;
- default:
- throwUnexpected();
- }
- return true;
- }
- bool selectNextParent()
- {
- switch (state)
- {
- case SGraph:
- state = SDone;
- break;
- case SActivity:
- childGraphIters.pop();
- //fall through
- case SChildGraph:
- case SSubGraph:
- case SEdge:
- popScope();
- state = popIterator();
- break;
- case SDone:
- return false;
- default:
- throwUnexpected();
- }
- return true;
- }
- protected:
- const ScopeFilter & filter;
- Owned<IConstWUGraphIterator> graphIter;
- Owned<IPropertyTree> curGraph;
- IArrayOf<IPropertyTreeIterator> treeIters;
- IArrayOf<IPropertyTreeIterator> childGraphIters;
- UnsignedArray scopeLengths;
- UnsignedArray stateStack;
- StringBuffer curScopeName;
- StringBuffer scopeId;
- StatisticScopeType scopeType = SSTnone;
- };
- static int compareWorkflow(IInterface * const * pLeft, IInterface * const * pRight)
- {
- IConstWorkflowItem * left = static_cast<IConstWorkflowItem *>(*pLeft);
- IConstWorkflowItem * right = static_cast<IConstWorkflowItem *>(*pRight);
- return left->queryWfid() - right->queryWfid();
- }
- static const char * trueToStr(bool value) { return value ? "true" : nullptr; }
- /*
- * An implementation of IConstWUScopeIterator for the workflow information.
- */
- class WorkflowStatisticsScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
- {
- public:
- WorkflowStatisticsScopeIterator(IConstWorkflowItemIterator * wfIter)
- {
- ForEach(*wfIter)
- workflow.append(*LINK(wfIter->query()));
- workflow.sort(compareWorkflow);
- }
- virtual bool first() override
- {
- if (workflow.empty())
- return false;
- curWorkflow = 0;
- return initWorkflowItem();
- }
- virtual bool next() override
- {
- if (!workflow.isItem(++curWorkflow))
- return false;
- return initWorkflowItem();
- }
- virtual bool nextSibling() override
- {
- return next();
- }
- virtual bool nextParent() override
- {
- curWorkflow = NotFound;
- return false;
- }
- virtual bool isValid() override
- {
- return workflow.isItem(curWorkflow);
- }
- virtual const char * queryScope() const override
- {
- return curScope.str();
- }
- virtual StatisticScopeType getScopeType() const override
- {
- return SSTworkflow;
- }
- virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
- {
- if (whichProperties & PTattributes)
- {
- {
- StringBuffer scratchpad;
- Owned<IWorkflowDependencyIterator> depends = workflow.item(curWorkflow).getDependencies();
- ForEach(*depends)
- visitor.noteAttribute(WaIdDependency, getValueText(depends->query(), scratchpad, WorkflowScopePrefix));
- }
- play(visitor, { WaIsScheduled, WaIdSuccess, WaIdFailure, WaIdRecovery, WaIdPersist, WaIdScheduled,
- WaPersistName, WaLabel, WaMode, WaType, WaState, WaCluster, WaCriticalSection });
- }
- }
- virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
- {
- return false;
- }
- virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
- {
- auto wf = &workflow.item(curWorkflow);
- StringBufferAdaptor adaptor(scratchpad);
- switch (attr)
- {
- case WaIdDependencyList:
- {
- bool first = true;
- Owned<IWorkflowDependencyIterator> depends = workflow.item(curWorkflow).getDependencies();
- ForEach(*depends)
- {
- if (first)
- scratchpad.append("[");
- else
- scratchpad.append(",");
- scratchpad.append('"').append(WorkflowScopePrefix).append(depends->query()).append('"');
- first = false;
- }
- if (first)
- return nullptr;
- scratchpad.append("]");
- return scratchpad.str();
- }
- case WaIsScheduled: return trueToStr(wf->isScheduled());
- case WaIdSuccess: return getWfidText(wf->querySuccess(), scratchpad);
- case WaIdFailure: return getWfidText(wf->queryFailure(), scratchpad);
- case WaIdRecovery: return getWfidText(wf->queryRecovery(), scratchpad);
- case WaIdPersist: return getWfidText(wf->queryPersistWfid(), scratchpad);
- case WaIdScheduled: return getWfidText(wf->queryScheduledWfid(), scratchpad);
- case WaPersistName: return queryOptString(wf->getPersistName(adaptor));
- case WaLabel: return queryOptString(wf->getLabel(adaptor));
- case WaMode: return wf->queryMode() != WFModeNormal ? queryWorkflowModeText(wf->queryMode()) : nullptr;
- case WaType: return wf->queryType() != WFTypeNormal ? queryWorkflowTypeText(wf->queryType()) : nullptr;
- case WaState: return queryWorkflowStateText(wf->queryState());
- case WaCluster: return queryOptString(wf->queryCluster(adaptor));
- case WaCriticalSection: return queryOptString(wf->getCriticalName(adaptor));
- /*
- The followng attributes are not generated - I'm not convinced they are very useful, but they could be added later.
- virtual bool isScheduledNow() const = 0;
- virtual IWorkflowEvent * getScheduleEvent() const = 0;
- virtual unsigned querySchedulePriority() const = 0;
- virtual bool hasScheduleCount() const = 0;
- virtual unsigned queryScheduleCount() const = 0;
- virtual unsigned queryRetriesAllowed() const = 0;
- virtual int queryPersistCopies() const = 0; // 0 - unmangled name, < 0 - use default, > 0 - max number
- virtual bool queryPersistRefresh() const = 0;
- virtual unsigned queryScheduleCountRemaining() const = 0;
- virtual unsigned queryRetriesRemaining() const = 0;
- virtual int queryFailCode() const = 0;
- virtual const char * queryFailMessage() const = 0;
- virtual const char * queryEventName() const = 0;
- virtual const char * queryEventExtra() const = 0;
- */
- }
- return nullptr;
- }
- virtual const char * queryHint(const char * kind) const
- {
- return nullptr;
- }
- protected:
- bool initWorkflowItem()
- {
- curScope.clear().append(WorkflowScopePrefix).append(workflow.item(curWorkflow).queryWfid());
- return true;
- }
- const char * getValueText(unsigned value, StringBuffer & scratchpad, const char * prefix = nullptr) const
- {
- return scratchpad.clear().append(prefix).append(value).str();
- }
- const char * queryOptString(IStringVal & value) const
- {
- const char * text = value.str();
- if (!text || !*text)
- return nullptr;
- return text;
- }
- const char * getWfidText(unsigned value, StringBuffer & scratchpad) const
- {
- if (!value)
- return nullptr;
- return scratchpad.clear().append(WorkflowScopePrefix).append(value).str();
- }
- void play(IWuScopeVisitor & visitor, const std::initializer_list<WuAttr> & attrs) const
- {
- StringBuffer scratchpad;
- for (auto attr : attrs)
- {
- const char * value = queryAttribute(attr, scratchpad.clear());
- if (value)
- visitor.noteAttribute(attr, value);
- }
- }
- protected:
- IArrayOf<IConstWorkflowItem> workflow;
- unsigned curWorkflow = 0;
- StringBuffer curScope;
- };
- /*
- * An implementation of IConstWUScopeIterator that combines results from multiple sources.
- */
- class CompoundStatisticsScopeIterator : public CInterfaceOf<IConstWUScopeIterator>
- {
- class VisitorMapper : implements IWuScopeVisitor
- {
- public:
- VisitorMapper(const WuScopeFilter & _filter, IWuScopeVisitor & _visitor) :
- filter(_filter), visitor(_visitor)
- {}
- virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
- {
- if (filter.includeStatistic(kind))
- visitor.noteStatistic(kind, value, extra);
- }
- virtual void noteAttribute(WuAttr attr, const char * value) override
- {
- if (filter.includeAttribute(attr))
- visitor.noteAttribute(attr, value);
- }
- virtual void noteHint(const char * kind, const char * value) override
- {
- if (filter.includeHint(kind))
- visitor.noteHint(kind, value);
- }
- virtual void noteException(IConstWUException & exception) override
- {
- visitor.noteException(exception);
- }
- protected:
- const WuScopeFilter & filter;
- IWuScopeVisitor & visitor;
- };
- public:
- CompoundStatisticsScopeIterator(const WuScopeFilter & _filter) : filter(_filter)
- {
- }
- void addIter(IConstWUScopeIterator * iter)
- {
- if (iter)
- {
- iters.append(OLINK(*iter));
- assertex(iters.ordinality() <= sizeof(activeIterMask)*8);
- }
- }
- virtual bool first() override
- {
- activeIterMask = 0;
- ForEachItemIn(i, iters)
- {
- if (iters.item(i).first())
- activeIterMask |= (1U << i);
- }
- return findNextScope();
- }
- virtual bool next() override
- {
- selectNext();
- return findNextScope();
- }
- virtual bool nextSibling() override
- {
- selectNextSibling();
- return findNextScope();
- }
- virtual bool nextParent() override
- {
- selectNextParent();
- return findNextScope();
- }
- virtual bool isValid() override
- {
- return (activeIterMask != 0);
- }
- virtual const char * queryScope() const override
- {
- return iters.item(firstMatchIter).queryScope();
- }
- virtual StatisticScopeType getScopeType() const override
- {
- return iters.item(firstMatchIter).getScopeType();
- }
- virtual void playProperties(IWuScopeVisitor & visitor, WuPropertyTypes whichProperties) override
- {
- VisitorMapper mappedVisitor(filter, visitor);
- whichProperties &= filter.properties;
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- iters.item(i).playProperties(mappedVisitor, whichProperties);
- }
- }
- virtual bool getStat(StatisticKind kind, unsigned __int64 & value) const override
- {
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- {
- if (iters.item(i).getStat(kind, value))
- return true;
- }
- }
- return false;
- }
- virtual const char * queryAttribute(WuAttr attr, StringBuffer & scratchpad) const override
- {
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- {
- const char * value = iters.item(i).queryAttribute(attr, scratchpad);
- if (value)
- return value;
- }
- }
- return nullptr;
- }
- virtual const char * queryHint(const char * kind) const override
- {
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- {
- const char * value = iters.item(i).queryHint(kind);
- if (value)
- return value;
- }
- }
- return nullptr;
- }
- inline bool iterMatchesCurrentScope(unsigned i) const { return ((1U << i) & matchIterMask) != 0; }
- inline bool isAlive(unsigned i) const { return ((1U << i) & activeIterMask) != 0; }
- protected:
- //Calculate which iterators contain the scope that should come next
- bool findNextScope()
- {
- for(;;)
- {
- if (activeIterMask == 0)
- return false;
- unsigned mask = 0;
- const char * scope = nullptr;
- ForEachItemIn(i, iters)
- {
- if (isAlive(i))
- {
- const char * iterScope = iters.item(i).queryScope();
- if (mask)
- {
- int compare = compareScopeName(scope, iterScope);
- if (compare == 0)
- {
- mask |= (1U << i);
- }
- else if (compare > 0)
- {
- scope = iterScope;
- mask = (1U << i);
- firstMatchIter = i;
- }
- }
- else
- {
- scope = iterScope;
- mask = (1U << i);
- firstMatchIter = i;
- }
- }
- }
- matchIterMask = mask;
- while (activeScopes)
- {
- const char * activeScope = activeScopes.tos();
- //If the next scope if not a child of one of the active scopes then that active scope will no longer match.
- if (compareScopes(scope, activeScope) & SCchild)
- break;
- activeScopes.pop();
- }
- //The top most scope will be the deepest. Check if the current scope is close enough to return as a match.
- bool include = false;
- if (activeScopes)
- {
- const char * activeScope = activeScopes.tos();
- //code above has ensured that this scope must be a child of (and therefore deeper) than activeScope
- unsigned nesting = queryScopeDepth(scope) - queryScopeDepth(activeScope);
- if (nesting <= filter.include.nestedDepth)
- include = true;
- }
- //Check to see if this is a new match
- if (filter.compareMatchScopes(scope) & SCequal)
- {
- if (!include)
- include = filter.include.matchedScope;
- //Only add it to the list of active scopes if it can match child elements
- if (filter.include.nestedDepth != 0)
- activeScopes.append(scope);
- }
- if (include && !filter.includeScope(scope))
- include = false;
- if (include && filter.requiredStats.size())
- {
- //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
- for (unsigned iReq=0; iReq < filter.requiredStats.size(); iReq++)
- {
- const StatisticValueFilter & cur = filter.requiredStats[iReq];
- unsigned __int64 value;
- if (getStat(cur.queryKind(), value))
- {
- if (!cur.matches(value))
- include = false;
- }
- else
- include = false;
- }
- }
- if (include && filter.requiredAttrs.size())
- {
- //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
- StringBuffer temp;
- for (unsigned iReq=0; iReq < filter.requiredAttrs.size(); iReq++)
- {
- const AttributeValueFilter & cur = filter.requiredAttrs[iReq];
- const char * value = queryAttribute(cur.queryKind(), temp.clear());
- if (!value || !cur.matches(value))
- {
- include = false;
- break;
- }
- }
- }
- if (include)
- return true;
- //MORE: Optimize based on filter.compareScope()
- selectNext();
- }
- }
- void checkScopeOrder(unsigned input, const char * prevScope)
- {
- if (isAlive(input))
- {
- const char * curScope = iters.item(input).queryScope();
- int compare = compareScopeName(prevScope, curScope);
- if (compare >= 0)
- throw MakeStringException(0, "Out of order (%u) scopes %s,%s = %d", input, prevScope, curScope, compare);
- }
- }
- void selectNext()
- {
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- {
- #ifdef _DEBUG
- StringBuffer prevScope(iters.item(i).queryScope());
- #endif
- if (!iters.item(i).next())
- activeIterMask &= ~(1U << i);
- #ifdef _DEBUG
- checkScopeOrder(i, prevScope);
- #endif
- }
- }
- }
- void selectNextSibling()
- {
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- {
- #ifdef _DEBUG
- StringBuffer prevScope(iters.item(i).queryScope());
- #endif
- if (!iters.item(i).nextSibling())
- activeIterMask &= ~(1U << i);
- #ifdef _DEBUG
- checkScopeOrder(i, prevScope);
- #endif
- }
- }
- }
- void selectNextParent()
- {
- ForEachItemIn(i, iters)
- {
- if (iterMatchesCurrentScope(i))
- {
- #ifdef _DEBUG
- StringBuffer prevScope(iters.item(i).queryScope());
- #endif
- if (!iters.item(i).nextParent())
- activeIterMask &= ~(1U << i);
- #ifdef _DEBUG
- checkScopeOrder(i, prevScope);
- #endif
- }
- }
- }
- protected:
- const WuScopeFilter & filter;
- IArrayOf<IConstWUScopeIterator> iters;
- unsigned curIter = 0;
- unsigned firstMatchIter = 0;
- unsigned matchIterMask = 0; // bit set of iterators which have a valid entry for the current scope
- unsigned activeIterMask = 0; // bit set of iterators which are still active
- StringArray activeScopes;
- };
- //---------------------------------------------------------------------------------------------------------------------
- class AggregateFilter
- {
- public:
- AggregateFilter(StatisticKind _search) : search(_search) {}
- inline bool matches(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) const
- {
- return (kind == search);
- }
- protected:
- StatisticKind search;
- //MORE: Allow filtering by scope?
- };
- class StatisticAggregator : public CInterfaceOf<IWuScopeVisitor>
- {
- public:
- StatisticAggregator(StatisticKind _search) : filter(_search) {}
- virtual void noteAttribute(WuAttr attr, const char * value) override { throwUnexpected(); }
- virtual void noteHint(const char * kind, const char * value) override { throwUnexpected(); }
- virtual void noteException(IConstWUException & exception) { throwUnexpected();}
- protected:
- AggregateFilter filter;
- };
- class SimpleAggregator : public StatisticAggregator
- {
- public:
- SimpleAggregator(StatisticKind _search) : StatisticAggregator(_search) {}
- virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
- {
- if (filter.matches(kind, value, extra))
- summary.noteValue(value);
- }
- //How should these be reported? Should there be a playAggregates(IWuAggregatedScopeVisitor)
- //with a noteAggregate(value, variant, value, grouping)?
- protected:
- StatsAggregation summary;
- };
- class SimpleReferenceAggregator : public StatisticAggregator
- {
- public:
- SimpleReferenceAggregator(StatisticKind _search, StatsAggregation & _summary) : StatisticAggregator(_search), summary(_summary) {}
- virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
- {
- if (filter.matches(kind, value, extra))
- summary.noteValue(value);
- }
- //How should these be reported? Should there be a playAggregates(IWuAggregatedScopeVisitor)
- //with a noteAggregate(value, variant, value, grouping)?
- protected:
- StatsAggregation & summary;
- };
- class GroupedAggregator : public StatisticAggregator
- {
- public:
- virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
- {
- if (filter.matches(kind, value, extra))
- {
- StatsAggregation & match = summary; // look up in hash table.
- match.noteValue(value);
- }
- }
- protected:
- //HashTable of (possible groupings->summary)
- StatsAggregation summary;
- };
- class CompoundAggregator : implements StatisticAggregator
- {
- public:
- virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra)
- {
- ForEachItemIn(i, aggregators)
- aggregators.item(i).noteStatistic(kind, value, extra);
- }
- protected:
- IArrayOf<StatisticAggregator> aggregators;
- };
- // Aggregate costs excluding all hThor costs
- // Note: the only reason that this class is required is that it is not possible to determine the creator
- // of a scope when iterating with IConstWUScopeIterator. (When this functionlity becomes available,
- // consider filtering within aggregateCost and eliminating this class.)
- class CostAggregatorExcludeHThor : public CInterfaceOf<IWuScopeVisitor>
- {
- public:
- virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & extra) override
- {
- if (extra.getCreatorType() != SCThthor)
- totalCost += value;
- }
- virtual void noteAttribute(WuAttr attr, const char * value) override { throwUnexpected(); }
- virtual void noteHint(const char * kind, const char * value) override { throwUnexpected(); }
- virtual void noteException(IConstWUException & exception) { throwUnexpected(); }
- virtual cost_type getTotalCost() const { return totalCost; }
- protected:
- cost_type totalCost = 0;
- };
- // Aggregrate Thor or hThor costs
- cost_type aggregateCost(const IConstWorkUnit * wu, const char *scope, bool excludeHThor)
- {
- // depth 1: workflow, depth 2: graph, depth 3: subgraph
- WuScopeFilter filter;
- if (scope && *scope) // All costs under specified scope (used to calculate all costs for a workflow)
- {
- filter.addScope(scope);
- filter.setIncludeNesting(3);
- filter.addOutputStatistic(StCostExecute);
- filter.addRequiredStat(StCostExecute);
- }
- else
- filter.addFilter("stat[CostExecute],depth[1..3],nested[0],where[CostExecute]");
- filter.addSource("global");
- filter.finishedFilter();
- Owned<IConstWUScopeIterator> it = &wu->getScopeIterator(filter);
- // Note: use of CostAggregatorExcludeHThor will be a slower, so use only when necessary
- if (excludeHThor)
- {
- CostAggregatorExcludeHThor aggregator;
- for (it->first(); it->isValid(); )
- {
- it->playProperties(aggregator);
- stat_type value;
- if (it->getStat(StCostExecute, value))
- it->nextSibling();
- else
- it->next();
- }
- return aggregator.getTotalCost();
- }
- else
- {
- cost_type totalCost = 0;
- for (it->first(); it->isValid(); )
- {
- stat_type value = 0;
- if (it->getStat(StCostExecute, value))
- {
- totalCost += value;
- it->nextSibling();
- }
- else
- it->next();
- }
- return totalCost;
- }
- }
- //aggregate disk costs from top-level subgraphs (when scope specified) or workflows (scope not specified)
- cost_type aggregateDiskAccessCost(const IConstWorkUnit * wu, const char *scope)
- {
- WuScopeFilter filter;
- if (!isEmptyString(scope))
- filter.addScope(scope);
- else
- filter.addScope(""); // Needed to match scope
- // when scope is a workflow, sum graph costs (or subgraph cost when no graph cost) to get workflow cost
- // (Costs from child graphs and activities should have been summed up to graph/subgraph level already)
- // when isEmptyString(scope), sum workflow costs (or graph cost when no workflow cost) to get global cost
- // (Costs from all levels below graph should be summed upto at least graph level already)
- // i.e. need 2 levels of nesting
- filter.setIncludeNesting(2);
- // includeNesting(2) needs just source "global". However, WuScopeFilter is incorrectly inferring the source as "global,stats",
- // causing too many of the stats to be pulled in and inefficiency. Here, explicitly set source to "global"
- filter.addSource("global");
- filter.addOutputStatistic(StCostFileAccess);
- filter.addRequiredStat(StCostFileAccess);
- filter.finishedFilter();
- Owned<IConstWUScopeIterator> it = &wu->getScopeIterator(filter);
- cost_type totalCost = 0;
- for (it->first(); it->isValid(); )
- {
- cost_type value = 0;
- if (it->getStat(StCostFileAccess, value))
- {
- totalCost += value;
- it->nextSibling();
- }
- else
- {
- it->next();
- }
- }
- return totalCost;
- }
- //---------------------------------------------------------------------------------------------------------------------
- //Extract an argument of the format abc[def[ghi...,]],... as (abc,def[...])
- static bool extractOption(const char * & finger, StringBuffer & option, StringBuffer & arg)
- {
- const char * start = finger;
- if (!*start)
- return false;
- const char * cur = start;
- const char * bra = nullptr;
- const char * end = nullptr;
- unsigned braDepth = 0;
- char next;
- arg.clear();
- for (;;)
- {
- next = *cur;
- if (!next || ((next == ',') && (braDepth == 0)))
- break;
- switch (next)
- {
- case '[':
- if (braDepth == 0)
- {
- if (bra)
- throw makeStringExceptionV(0, "Multiple [ in filter : %s", bra);
- bra = cur;
- }
- braDepth++;
- break;
- case ']':
- if (braDepth > 0)
- {
- if (--braDepth == 0)
- {
- end = cur;
- arg.append(cur - (bra+1), bra+1);
- }
- }
- break;
- }
- cur++;
- }
- if (braDepth != 0)
- throw makeStringExceptionV(0, "Mismatched ] in filter : %s", start);
- option.clear();
- if (bra)
- {
- if (cur != end+1)
- throw makeStringExceptionV(0, "Text follows closing bracket: %s", end);
- option.append(bra-start, start);
- }
- else
- option.append(cur-start, start);
- if (next)
- finger = cur+1;
- else
- finger = cur;
- return true;
- }
- static unsigned readOptValue(const char * start, const char * end, unsigned dft, const char * type)
- {
- if (start == end)
- return dft;
- char * next;
- unsigned value = (unsigned)strtoll(start, &next, 10);
- if (next != end)
- throw makeStringExceptionV(0, "Unexpected characters in %s option '%s'", type, next);
- return value;
- }
- static unsigned readValue(const char * start, const char * type)
- {
- if (*start == '\0')
- throw makeStringExceptionV(0, "Expected a value for the %s option", type);
- char * next;
- unsigned value = (unsigned)strtoll(start, &next, 10);
- if (*next != '\0')
- throw makeStringExceptionV(0, "Unexpected characters in %s option '%s'", type, next);
- return value;
- }
- StringBuffer & AttributeValueFilter::describe(StringBuffer & out) const
- {
- out.append(queryWuAttributeName(attr));
- if (value)
- out.append("=").append(value);
- return out;
- }
- /*
- Scope service matching Syntax: * indicates
- Which items are matched:
- scope[<scope-id>]* | stype[<scope-type>]* | id[<id>]* - which scopes should be matched?
- depth[n | low..high] - range of depths to search for a match
- source[global|stats|graph|all]* - which sources to search within the workunit
- where[<statistickind> | <statistickind> (=|<|<=|>|>=) value | <statistickind>=low..high] - a statistic filter
- Which items are include in the results:
- matched[true|false] - are the matched scopes returned?
- nested[<depth>|all] - how deep within a scope should be matched (default = 0 if matched[true], all if matched[false])
- includetype[<scope-type>] - which scope types should be included?
- Which properties of the items are returned:
- properties[statistics|hints|attributes|scope|all]
- statistic[<statistic-kind>|none|all] - include statistic
- attribute[<attribute-name>|none|all] - include attribute
- hint[<hint-name>] - include hint
- property[<statistic-kind>|<attribute-name>|<hint-name>] - include property
- measure[<measure>] - all statistics with a particular measure
- version[<version>] - minimum version to return
- */
- enum { FOscope, FOstype, FOid, FOdepth, FOsource, FOwhere, FOmatched, FOnested, FOinclude, FOproperties, FOstatistic, FOattribute, FOhint, FOproperty, FOmeasure, FOversion, FOunknown };
- //Some of the following contains aliases for the same option e.g. stat and statistic
- static constexpr EnumMapping filterOptions[] = {
- { FOscope, "scope" }, { FOstype, "stype" }, { FOid, "id" },
- { FOdepth, "depth" }, { FOsource, "source" }, { FOwhere, "where" },
- { FOmatched, "matched" }, { FOnested, "nested" },
- { FOinclude, "include" }, { FOinclude, "includetype" },
- { FOproperties, "props" }, { FOproperties, "properties" }, // some aliases
- { FOstatistic, "stat" }, { FOstatistic, "statistic" }, { FOattribute, "attr" }, { FOattribute, "attribute" }, { FOhint, "hint" },
- { FOproperty, "prop" }, { FOproperty, "property" },
- { FOmeasure, "measure" }, { FOversion, "version" }, { 0, nullptr} };
- static constexpr EnumMapping sourceMappings[] = {
- { SSFsearchGlobalStats, "global" }, { SSFsearchGraphStats, "stats" }, { SSFsearchGraphStats, "statistics" }, { SSFsearchGraph, "graph" }, { SSFsearchExceptions, "exception" }, { SSFsearchWorkflow, "workflow" },
- { (int)SSFsearchAll, "all" }, { 0, nullptr } };
- static constexpr EnumMapping propertyMappings[] = {
- { PTstatistics, "stat" }, { PTstatistics, "statistic" }, { PTattributes, "attr" }, { PTattributes, "attribute" }, { PThints, "hint" },{ PTnotes, "note" },
- { PTstatistics, "stats" }, { PTstatistics, "statistics" }, { PTattributes, "attrs" }, { PTattributes, "attributes" }, { PThints, "hints" }, { PTnotes, "notes" },
- { PTnone, "none" }, { PTscope, "scope" }, { PTall, "all" }, { 0, nullptr } };
- WuScopeSourceFlags querySource(const char * source) { return (WuScopeSourceFlags)getEnum(source, sourceMappings, SSFunknown); }
- const char * querySourceText(WuScopeSourceFlags source) { return getEnumText(source, sourceMappings, nullptr); }
- WuScopeFilter::WuScopeFilter(const char * filter)
- {
- addFilter(filter);
- finishedFilter();
- }
- WuScopeFilter & WuScopeFilter::addFilter(const char * filter)
- {
- checkModifiable();
- if (!filter)
- return *this;
- StringBuffer option;
- StringBuffer arg;
- while (extractOption(filter, option, arg))
- {
- switch (getEnum(option, filterOptions, FOunknown))
- {
- case FOscope:
- addScope(arg);
- break;
- case FOstype:
- addScopeType(arg);
- break;
- case FOid:
- addId(arg);
- break;
- case FOdepth:
- {
- //Allow depth[n], depth[a,b] or depth[a..b]
- const char * comma = strchr(arg, ',');
- const char * dotdot = strstr(arg, "..");
- if (comma)
- {
- unsigned low = readOptValue(arg, comma, 0, "depth");
- if (comma[1])
- {
- scopeFilter.setDepth(low, readValue(comma+1, "depth"));
- }
- else
- scopeFilter.setDepth(low, UINT_MAX);
- }
- else if (dotdot)
- {
- unsigned low = readOptValue(arg, dotdot, 0, "depth");
- if (dotdot[2])
- scopeFilter.setDepth(low, readValue(dotdot+2, "depth"));
- else
- scopeFilter.setDepth(low, UINT_MAX);
- }
- else
- {
- scopeFilter.setDepth(readValue(arg, "depth"));
- }
- break;
- }
- case FOsource:
- addSource(arg);
- break;
- case FOwhere: // where[stat<op>value]
- addRequiredStat(arg);
- break;
- case FOmatched:
- setIncludeMatch(strToBool(arg));
- break;
- case FOnested:
- if (strieq(arg, "all"))
- setIncludeNesting(UINT_MAX);
- else if (isdigit(*arg))
- setIncludeNesting(atoi(arg));
- else
- throw makeStringExceptionV(0, "Expected a value for the nesting depth: %s", arg.str());
- break;
- case FOinclude:
- setIncludeScopeType(arg);
- break;
- case FOproperties:
- {
- WuPropertyTypes prop = (WuPropertyTypes)getEnum(arg, propertyMappings, PTunknown);
- if (prop == PTunknown)
- throw makeStringExceptionV(0, "Unexpected properties '%s'", arg.str());
- addOutputProperties(prop);
- break;
- }
- case FOstatistic:
- addOutputStatistic(arg);
- break;
- case FOattribute:
- addOutputAttribute(arg);
- break;
- case FOhint:
- addOutputHint(arg);
- break;
- case FOproperty:
- addOutput(arg);
- break;
- case FOmeasure:
- setMeasure(arg);
- break;
- case FOversion:
- if (isdigit(*arg))
- minVersion = atoi64(arg);
- else
- throw makeStringExceptionV(0, "Expected a value for the version: %s", arg.str());
- break;
- default:
- throw makeStringExceptionV(0, "Unrecognised filter option: %s", option.str());
- }
- }
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addScope(const char * scope)
- {
- checkModifiable();
- if (scope)
- {
- validateScope(scope);
- scopeFilter.addScope(scope);
- }
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addScopeType(StatisticScopeType scopeType)
- {
- checkModifiable();
- scopeFilter.addScopeType(scopeType);
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addScopeType(const char * scopeType)
- {
- checkModifiable();
- if (scopeType)
- {
- StatisticScopeType sst = queryScopeType(scopeType, SSTmax);
- if (sst == SSTmax)
- throw makeStringExceptionV(0, "Unrecognised scope type '%s'", scopeType);
- scopeFilter.addScopeType(sst);
- }
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addId(const char * id)
- {
- checkModifiable();
- validateScopeId(id);
- scopeFilter.addId(id);
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addOutput(const char * prop)
- {
- checkModifiable();
- WuAttr attr = queryWuAttribute(prop, WaNone);
- if (attr != WaNone)
- {
- WuAttr singleKindAttr = getSingleKindOfListAttribute(attr);
- if (singleKindAttr != WaNone)
- addOutputAttribute(singleKindAttr);
- else
- addOutputAttribute(attr);
- } else if (queryStatisticKind(prop, StMax) != StMax)
- addOutputStatistic(prop);
- else
- addOutputHint(prop);
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addOutputStatistic(const char * prop)
- {
- checkModifiable();
- if (!prop)
- return *this;
- StatisticKind kind = queryStatisticKind(prop, StMax);
- if (kind == StMax)
- throw makeStringExceptionV(0, "Unrecognised statistic '%s'", prop);
- return addOutputStatistic(kind);
- }
- WuScopeFilter & WuScopeFilter::addOutputStatistic(StatisticKind stat)
- {
- checkModifiable();
- if (stat != StKindNone)
- {
- if (stat != StKindAll)
- desiredStats.append(stat);
- else
- desiredStats.kill();
- properties |= PTstatistics;
- }
- else
- properties &= ~PTstatistics;
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addOutputAttribute(const char * prop)
- {
- checkModifiable();
- if (!prop)
- return *this;
- WuAttr attr = queryWuAttribute(prop, WaMax);
- if (attr == WaMax)
- throw makeStringExceptionV(0, "Unrecognised attribute '%s'", prop);
- return addOutputAttribute(attr);
- }
- WuScopeFilter & WuScopeFilter::addOutputAttribute(WuAttr attr)
- {
- checkModifiable();
- if (attr != WaNone)
- {
- if (attr != WaAll)
- desiredAttrs.append(attr);
- else
- desiredAttrs.kill();
- properties |= PTattributes;
- }
- else
- properties &= ~PTattributes;
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addOutputHint(const char * prop)
- {
- checkModifiable();
- if (strieq(prop, "none"))
- {
- desiredHints.kill();
- properties &= ~PThints;
- }
- else
- {
- if (!strieq(prop, "all"))
- desiredHints.append(prop);
- else
- desiredHints.kill();
- properties |= PThints;
- }
- return *this;
- }
- WuScopeFilter & WuScopeFilter::setIncludeMatch(bool value)
- {
- checkModifiable();
- include.matchedScope = value;
- return *this;
- }
- WuScopeFilter & WuScopeFilter::setIncludeNesting(unsigned depth)
- {
- checkModifiable();
- include.nestedDepth = depth;
- return *this;
- }
- WuScopeFilter & WuScopeFilter::setIncludeScopeType(const char * scopeType)
- {
- checkModifiable();
- if (scopeType)
- {
- StatisticScopeType sst = queryScopeType(scopeType, SSTmax);
- if (sst == SSTmax)
- throw makeStringExceptionV(0, "Unrecognised scope type '%s'", scopeType);
- include.scopeTypes.append(sst);
- }
- return *this;
- }
- WuScopeFilter & WuScopeFilter::setMeasure(const char * measure)
- {
- checkModifiable();
- if (measure)
- {
- desiredMeasure = queryMeasure(measure, SMeasureNone);
- if (desiredMeasure == SMeasureNone)
- throw makeStringExceptionV(0, "Unrecognised measure '%s'", measure);
- properties |= PTstatistics;
- }
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addOutputProperties(WuPropertyTypes mask)
- {
- checkModifiable();
- if (properties == PTnone)
- properties = mask;
- else
- properties |= mask;
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addRequiredStat(StatisticKind statKind, stat_type lowValue, stat_type highValue)
- {
- checkModifiable();
- requiredStats.emplace_back(statKind, lowValue, highValue);
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addRequiredStat(StatisticKind statKind)
- {
- checkModifiable();
- requiredStats.emplace_back(statKind, 0, MaxStatisticValue);
- return *this;
- }
- WuScopeFilter & WuScopeFilter::addRequiredAttr(WuAttr attr, const char * value)
- {
- checkModifiable();
- requiredAttrs.emplace_back(attr, value);
- return *this;
- }
- //process a filter in one of the following forms:
- // <statistic-name>
- // <statistic-name> (=|<|<=|>|>=) <value>
- // <statistic-name>=[<low>]..[<high>]
- void WuScopeFilter::addRequiredStat(const char * filter)
- {
- checkModifiable();
- const char * stat = filter;
- const char * cur = stat;
- while (isalpha(*cur))
- cur++;
- StringBuffer statisticName(cur-stat, stat);
- //Skip any spaces before a comparison operator.
- while (*cur && isspace(*cur))
- cur++;
- StatisticKind statKind = queryStatisticKind(statisticName, StKindNone);
- if (statKind == StKindNone)
- {
- WuAttr attr = queryWuAttribute(statisticName, WaNone);
- if (attr == WaNone)
- throw makeStringExceptionV(0, "Unknown property name '%s'", statisticName.str());
- if (*cur == '=')
- requiredAttrs.emplace_back(attr, cur+1);
- else if (*cur == '\0')
- requiredAttrs.emplace_back(attr, nullptr);
- else
- throw makeStringExceptionV(0, "Unknown attribute comparison '%s'", cur);
- return;
- }
- //Save the operator, and skip over any non digits.
- const char * op = cur;
- switch (*op)
- {
- case '=':
- cur++;
- break;
- case '<':
- case '>':
- if (op[1] == '=')
- cur += 2;
- else
- cur++;
- break;
- case '\0':
- break;
- default:
- throw makeStringExceptionV(0, "Unknown comparison '%s'", op);
- }
- const char * next;
- stat_type value = readStatisticValue(cur, &next, queryMeasure(statKind));
- stat_type lowValue = 0;
- stat_type highValue = MaxStatisticValue;
- switch (op[0])
- {
- case '=':
- {
- //Allow a,b or a..b to specify a range - either bound may be omitted.
- if (next[0] == ',')
- {
- lowValue = value;
- next++;
- if (*next != '\0')
- highValue = readStatisticValue(next, &next, queryMeasure(statKind));
- }
- else if (strncmp(next, "..", 2) == 0)
- {
- lowValue = value;
- next += 2;
- if (*next != '\0')
- highValue = readStatisticValue(next, &next, queryMeasure(statKind));
- }
- else
- {
- lowValue = value;
- highValue = value;
- }
- break;
- }
- case '<':
- if (op[1] == '=')
- highValue = value;
- else
- highValue = value-1;
- break;
- case '>':
- if (op[1] == '=')
- lowValue = value;
- else
- lowValue = value+1;
- break;
- }
- if (*next)
- throw makeStringExceptionV(0, "Trailing characters in where '%s'", next);
- requiredStats.emplace_back(statKind, lowValue, highValue);
- }
- WuScopeFilter & WuScopeFilter::addSource(const char * source)
- {
- checkModifiable();
- WuScopeSourceFlags mask = querySource(source);
- if (mask == SSFunknown)
- throw makeStringExceptionV(0, "Unexpected source '%s'", source);
- if (!mask)
- sourceFlags = mask;
- else
- sourceFlags |= mask;
- return *this;
- }
- WuScopeFilter & WuScopeFilter::setDepth(unsigned low, unsigned high)
- {
- checkModifiable();
- scopeFilter.setDepth(low, high);
- return *this;
- }
- bool WuScopeFilter::matchOnly(StatisticScopeType scopeType) const
- {
- //If there is a post filter on the scopeType, then check if it matches
- if (include.scopeTypes.ordinality() == 1)
- return (include.scopeTypes.item(0) == scopeType);
- //If filter doesn't match nested items, then check if the scope filter matches.
- if (include.nestedDepth == 0)
- {
- if (scopeFilter.matchOnly(scopeType))
- return true;
- }
- return false;
- }
- void WuScopeFilter::reportModifyTooLate()
- {
- throw makeStringException(WUERR_ModifyFilterAfterFinalize, "Attempting to modify WuScopefilter after finish() has been called");
- }
- //Called once the filter has been updated to optimize the filter
- void WuScopeFilter::finishedFilter()
- {
- if (optimized)
- throw makeStringException(WUERR_FinalizeAfterFinalize, "Calling finishedFilter() more than once");
- scopeFilter.finishedFilter();
- if ((include.nestedDepth == 0) && !include.matchedScope)
- include.nestedDepth = UINT_MAX;
- preFilterScope = include.matchedScope && (include.nestedDepth == 0);
- if (scopeFilter.canAlwaysPreFilter())
- preFilterScope = true;
- //If the source flags have not been explicitly set then calculate which sources will provide the results
- if (!sourceFlags)
- {
- sourceFlags = SSFsearchAll;
- //Use the other options to reduce the number of elements that are searched.
- //If not interested in scopes on their own then...
- if (!(properties & PTscope))
- {
- //Global stats and graph stats only contain stats => remove if not interested in them
- if (!(properties & PTstatistics) && (requiredStats.size() == 0))
- sourceFlags &= ~(SSFsearchGlobalStats|SSFsearchGraphStats);
- //graph, workflow only contains attributes and hints => remove if not interested
- if (!(properties & (PTattributes|PThints)) && (requiredAttrs.size() == 0))
- sourceFlags &= ~(SSFsearchGraph|SSFsearchWorkflow);
- }
- if (!(properties & PTnotes))
- sourceFlags &= ~SSFsearchExceptions;
- }
- //Optimize sources if they haven't been explicitly specified
- //Most of the following are dependent on internal knowledge of the way that information is represented
- if (matchOnly(SSTworkflow))
- {
- //Workflow is not nested within a graph
- sourceFlags &= ~(SSFsearchGraph|SSFsearchGraphStats);
- setDepth(1, 1);
- }
- else if (matchOnly(SSTgraph))
- {
- //Graph starts are stored globally, not in the graph stats.
- sourceFlags &= ~(SSFsearchGraphStats|SSFsearchWorkflow);
- if (!(properties & (PTattributes|PThints)))
- sourceFlags &= ~(SSFsearchGraph);
- //This should really be setDepth(2,2) but workunits prior to 7.4 did not have graph ids prefixed by the wfid
- //Remove once 7.2 is a distant memory (see HPCC-22887)
- setDepth(1, 2);
- }
- else if (matchOnly(SSTsubgraph))
- {
- sourceFlags &= ~(SSFsearchWorkflow);
- }
- else if (matchOnly(SSTcompilestage))
- {
- //compile stages are not stored in the graph
- sourceFlags &= ~(SSFsearchGraphStats|SSFsearchGraph|SSFsearchWorkflow);
- }
- else if (matchOnly(SSTactivity))
- {
- //information about activities is not stored globally
- sourceFlags &= ~(SSFsearchGlobalStats|SSFsearchWorkflow);
- }
- // Everything stored in the graphs stats has a depth of 3 or more - so ignore if there are not matches in that depth
- if (include.nestedDepth == 0)
- {
- if (scopeFilter.compareDepth(3) > 0) // 3 is larger than any scope in the filter
- sourceFlags &= ~(SSFsearchGraphStats);
- else if (scopeFilter.compareDepth(3) == 0) // 3 matches the maximum scope in the filter
- {
- //Subgraph WhenStarted and TimeElapsed are stored globally. If that is all that is required
- //then do not include the subgraph timings
- if (desiredStats.ordinality())
- {
- bool needGraphStats = false;
- ForEachItemIn(i, desiredStats)
- {
- unsigned stat = desiredStats.item(i);
- if ((stat != StWhenStarted) && (stat != StTimeElapsed))
- needGraphStats = true;
- }
- if (!needGraphStats)
- {
- sourceFlags &= ~(SSFsearchGraphStats);
- if (matchOnly(SSTsubgraph))
- sourceFlags &= SSFsearchGlobalStats;
- }
- }
- }
- }
- if (scopeFilter.compareDepth(1) < 0)
- {
- //If minimum match depth is > 1 then it will never match workflow
- sourceFlags &= ~(SSFsearchWorkflow);
- }
- //The xml graph is never updated, so do not check it if minVersion != 0
- if (minVersion != 0)
- {
- sourceFlags &= ~(SSFsearchGraph|SSFsearchWorkflow);
- }
- optimized = true;
- }
- bool WuScopeFilter::includeStatistic(StatisticKind kind) const
- {
- if (!(properties & PTstatistics))
- return false;
- if ((desiredMeasure != SMeasureAll) && (queryMeasure(kind) != desiredMeasure))
- return false;
- if (desiredStats.empty())
- return true;
- return desiredStats.contains(kind);
- }
- bool WuScopeFilter::includeAttribute(WuAttr attr) const
- {
- if (!(properties & PTattributes))
- return false;
- if (desiredAttrs.empty())
- return true;
- return desiredAttrs.contains(attr);
- }
- bool WuScopeFilter::includeHint(const char * kind) const
- {
- if (!(properties & PThints))
- return false;
- if (desiredHints.empty())
- return true;
- return desiredHints.contains(kind);
- }
- bool WuScopeFilter::includeScope(const char * scope) const
- {
- if (include.scopeTypes)
- {
- const char * tail = queryScopeTail(scope);
- StatsScopeId id(tail);
- if (!include.scopeTypes.contains(id.queryScopeType()))
- return false;
- }
- return true;
- }
- const static ScopeFilter nullScopeFilter {};
- const ScopeFilter & WuScopeFilter::queryIterFilter() const
- {
- if (preFilterScope)
- return scopeFilter;
- else
- return nullScopeFilter;
- }
- ScopeCompare WuScopeFilter::compareMatchScopes(const char * scope) const
- {
- return scopeFilter.compare(scope);
- }
- StringBuffer & WuScopeFilter::describe(StringBuffer & out) const
- {
- scopeFilter.describe(out);
- if (requiredStats.size() || requiredAttrs.size())
- {
- out.append(",where[");
- bool first = false;
- for (const auto & stat : requiredStats)
- {
- if (!first)
- out.append(",");
- stat.describe(out);
- first = false;
- }
- for (const auto & attr : requiredAttrs)
- {
- if (!first)
- out.append(",");
- attr.describe(out);
- first = false;
- }
- out.append("]");
- }
- {
- StringBuffer sources;
- for (unsigned mask=1; mask; mask *= 2)
- {
- if (sourceFlags & mask)
- {
- const char * source = querySourceText((WuScopeSourceFlags)mask);
- if (source)
- sources.append(",").append(source);
- }
- }
- if (sources)
- out.append(",source[").append(sources.str()+1).append("]");
- }
- if (include.nestedDepth != UINT_MAX)
- out.appendf(",nested[%u]", include.nestedDepth);
- if (include.scopeTypes)
- {
- out.append(",include[");
- ForEachItemIn(i, include.scopeTypes)
- {
- if (i)
- out.append(",");
- out.append(queryScopeTypeName((StatisticScopeType)include.scopeTypes.item(i)));
- }
- out.append("]");
- }
- {
- StringBuffer props;
- if (properties == PTnone)
- props.append(",none");
- else if (properties == PTall)
- props.append(",all");
- else
- {
- if (properties & PTstatistics)
- props.append(",stat");
- if (properties & PTattributes)
- props.append(",attr");
- if (properties & PThints)
- props.append(",hint");
- if (properties & PTscope)
- props.append(",scope");
- if (properties & PTnotes)
- props.append(",note");
- }
- out.append(",properties[").append(props.str()+1).append("]");
- }
- if (desiredStats)
- {
- out.append(",stat[");
- ForEachItemIn(i, desiredStats)
- {
- if (i)
- out.append(",");
- out.append(queryStatisticName((StatisticKind)desiredStats.item(i)));
- }
- out.append("]");
- }
- if (desiredAttrs)
- {
- out.append(",attr[");
- ForEachItemIn(i, desiredAttrs)
- {
- if (i)
- out.append(",");
- out.append(queryWuAttributeName((WuAttr)desiredAttrs.item(i)));
- }
- out.append("]");
- }
- if (desiredHints)
- {
- out.append(",hint[");
- ForEachItemIn(i, desiredHints)
- {
- if (i)
- out.append(",");
- out.append(desiredHints.item(i));
- }
- out.append("]");
- }
- if (desiredMeasure != SMeasureAll)
- out.append(",measure[").append(queryMeasureName(desiredMeasure)).append("]");
- if (minVersion != 0)
- out.appendf(",version(%" I64F "u)", minVersion);
- return out;
- }
- //--------------------------------------------------------------------------------------------------------------------
- EnumMapping states[] = {
- { WUStateUnknown, "unknown" },
- { WUStateCompiled, "compiled" },
- { WUStateRunning, "running" },
- { WUStateCompleted, "completed" },
- { WUStateFailed, "failed" },
- { WUStateArchived, "archived" },
- { WUStateAborting, "aborting" },
- { WUStateAborted, "aborted" },
- { WUStateBlocked, "blocked" },
- { WUStateSubmitted, "submitted" },
- { WUStateScheduled, "scheduled" },
- { WUStateCompiling, "compiling" },
- { WUStateWait, "wait" },
- { WUStateUploadingFiles, "uploading_files" },
- { WUStateDebugPaused, "debugging" },
- { WUStateDebugRunning, "debug_running" },
- { WUStatePaused, "paused" },
- { WUStateSize, NULL }
- };
- EnumMapping actions[] = {
- { WUActionUnknown, "unknown" },
- { WUActionCompile, "compile" },
- { WUActionCheck, "check" },
- { WUActionRun, "run" },
- { WUActionExecuteExisting, "execute" },
- { WUActionPause, "pause" },
- { WUActionPauseNow, "pausenow" },
- { WUActionResume, "resume" },
- { WUActionSize, NULL },
- };
- EnumMapping priorityClasses[] = {
- { PriorityClassUnknown, "unknown" },
- { PriorityClassLow, "low" },
- { PriorityClassNormal, "normal" },
- { PriorityClassHigh, "high" },
- { PriorityClassSize, NULL },
- };
- const char * getWorkunitStateStr(WUState state)
- {
- dbgassertex(state < WUStateSize);
- return states[state].str; // MORE - should be using getEnumText, or need to take steps to ensure values remain contiguous and in order.
- }
- void setEnum(IPropertyTree *p, const char *propname, int value, const EnumMapping *map)
- {
- const char *defval = map->str;
- while (map->str)
- {
- if (value==map->val)
- {
- p->setProp(propname, map->str);
- return;
- }
- map++;
- }
- assertex(!"Unexpected value in setEnum");
- p->setProp(propname, defval);
- }
- static int getEnum(const IPropertyTree *p, const char *propname, const EnumMapping *map)
- {
- return getEnum(p->queryProp(propname),map);
- }
- const char * getWorkunitActionStr(WUAction action)
- {
- return getEnumText(action, actions);
- }
- WUAction getWorkunitAction(const char *actionStr)
- {
- return (WUAction) getEnum(actionStr, actions);
- }
- //==========================================================================================
- class CLightweightWorkunitInfo : public CInterfaceOf<IConstWorkUnitInfo>
- {
- public:
- CLightweightWorkunitInfo(IPropertyTree &p)
- {
- wuid.set(p.queryName());
- user.set(p.queryProp("@submitID"));
- jobName.set(p.queryProp("@jobName"));
- clusterName.set(p.queryProp("@clusterName"));
- timeScheduled.set(p.queryProp("@timeScheduled"));
- state = (WUState) getEnum(&p, "@state", states);
- action = (WUAction) getEnum(&p, "Action", actions);
- priority = (WUPriorityClass) getEnum(&p, "@priorityClass", priorityClasses);
- priorityLevel = calcPriorityValue(&p);
- wuscope.set(p.queryProp("@scope"));
- appvalues.loadBranch(&p,"Application");
- totalThorTime = (unsigned)nanoToMilli(extractTimeCollatable(p.queryProp("@totalThorTime"), nullptr));
- _isProtected = p.getPropBool("@protected", false);
- costExecute = p.getPropInt64("@costExecute");
- costFileAccess = p.getPropInt64("@costFileAccess");
- }
- virtual const char *queryWuid() const { return wuid.str(); }
- virtual const char *queryUser() const { return user.str(); }
- virtual const char *queryJobName() const { return jobName.str(); }
- virtual const char *queryClusterName() const { return clusterName.str(); }
- virtual const char *queryWuScope() const { return wuscope.str(); }
- virtual WUState getState() const { return state; }
- virtual const char *queryStateDesc() const { return getEnumText(state, states); }
- virtual WUAction getAction() const { return action; }
- virtual const char *queryActionDesc() const { return getEnumText(action, actions); }
- virtual WUPriorityClass getPriority() const { return priority; }
- virtual const char *queryPriorityDesc() const { return getEnumText(priority, priorityClasses); }
- virtual int getPriorityLevel() const { return priorityLevel; }
- virtual bool isProtected() const { return _isProtected; }
- virtual cost_type getExecuteCost() const { return costExecute; }
- virtual cost_type getFileAccessCost() const { return costFileAccess; }
- virtual IJlibDateTime & getTimeScheduled(IJlibDateTime & val) const
- {
- if (timeScheduled.length())
- val.setGmtString(timeScheduled.str());
- return val;
- }
- virtual unsigned getTotalThorTime() const { return totalThorTime; };
- virtual IConstWUAppValueIterator & getApplicationValues() const { return *new CArrayIteratorOf<IConstWUAppValue,IConstWUAppValueIterator> (appvalues, 0, (IConstWorkUnitInfo *) this); };
- protected:
- StringAttr wuid, user, jobName, clusterName, timeScheduled, wuscope;
- mutable CachedWUAppValues appvalues;
- unsigned totalThorTime;
- WUState state;
- WUAction action;
- WUPriorityClass priority;
- int priorityLevel;
- bool _isProtected;
- unsigned __int64 costExecute;
- unsigned __int64 costFileAccess;
- };
- extern IConstWorkUnitInfo *createConstWorkUnitInfo(IPropertyTree &p)
- {
- return new CLightweightWorkunitInfo(p);
- }
- class CDaliWorkUnit;
- class CDaliWuGraphStats : public CWuGraphStats
- {
- public:
- CDaliWuGraphStats(const CDaliWorkUnit* _owner, StatisticCreatorType _creatorType, const char * _creator, unsigned _wfid, const char * _rootScope, unsigned _id, bool _merge)
- : CWuGraphStats(_creatorType, _creator, _wfid, _rootScope, _id, _merge), owner(_owner), graphName(_rootScope), wfid(_wfid)
- {
- }
- protected:
- virtual IPropertyTree &queryProgressTree() override;
- const CDaliWorkUnit *owner;
- Owned<IRemoteConnection> conn;
- StringAttr graphName;
- unsigned wfid;
- };
- class CLocalWuGraphStats : public CWuGraphStats
- {
- public:
- CLocalWuGraphStats(IPropertyTree *_p, StatisticCreatorType _creatorType, const char * _creator, unsigned _wfid, const char * _rootScope, unsigned _id, bool _merge)
- : CWuGraphStats(_creatorType, _creator, _wfid, _rootScope, _id, _merge), graphName(_rootScope), p(_p)
- {
- }
- protected:
- virtual IPropertyTree &queryProgressTree() override
- {
- IPropertyTree *progress = p->queryPropTree("GraphProgress");
- if (!progress)
- progress = p->addPropTree("GraphProgress");
- IPropertyTree *graph = progress->queryPropTree(graphName);
- if (!graph)
- graph = progress->addPropTree(graphName);
- return *graph;
- }
- StringAttr graphName;
- Owned<IPropertyTree> p;
- };
- CWorkUnitWatcher::CWorkUnitWatcher(IWorkUnitSubscriber *_subscriber, WUSubscribeOptions flags, const char *wuid) : subscriber(_subscriber)
- {
- abortId = 0;
- stateId = 0;
- actionId = 0;
- assertex((flags & ~SubscribeOptionAbort) == 0);
- if (flags & SubscribeOptionAbort)
- {
- VStringBuffer xpath("/WorkUnitAborts/%s", wuid);
- abortId = querySDS().subscribe(xpath.str(), *this, false, true);
- }
- }
- CWorkUnitWatcher::~CWorkUnitWatcher()
- {
- assertex(abortId==0 && stateId==0 && actionId==0);
- }
- void CWorkUnitWatcher::unsubscribe()
- {
- CriticalBlock b(crit);
- if (abortId)
- querySDS().unsubscribe(abortId);
- if (stateId)
- querySDS().unsubscribe(stateId);
- if (actionId)
- querySDS().unsubscribe(actionId);
- abortId = 0;
- stateId = 0;
- actionId = 0;
- }
- void CWorkUnitWatcher::notify(SubscriptionId id, const char *xpath, SDSNotifyFlags flags, unsigned valueLen, const void *valueData)
- {
- CriticalBlock b(crit);
- if (id==stateId)
- subscriber->notify(SubscribeOptionState, valueLen, valueData);
- else if (id==actionId)
- subscriber->notify(SubscribeOptionAction, valueLen, valueData);
- else if (id==abortId)
- subscriber->notify(SubscribeOptionAbort, valueLen, valueData);
- }
- class CDaliWorkUnitWatcher : public CWorkUnitWatcher
- {
- public:
- CDaliWorkUnitWatcher(IWorkUnitSubscriber *_subscriber, WUSubscribeOptions flags, const char *wuid)
- : CWorkUnitWatcher(_subscriber, (WUSubscribeOptions) (flags & SubscribeOptionAbort), wuid)
- {
- if (flags & SubscribeOptionState)
- {
- VStringBuffer xpath("/WorkUnits/%s/State", wuid);
- stateId = querySDS().subscribe(xpath.str(), *this);
- }
- if (flags & SubscribeOptionAction)
- {
- VStringBuffer xpath("/WorkUnits/%s/Action", wuid);
- actionId = querySDS().subscribe(xpath.str(), *this);
- }
- }
- };
- void CPersistedWorkUnit::subscribe(WUSubscribeOptions options)
- {
- CriticalBlock block(crit);
- assertex(options==SubscribeOptionAbort);
- if (!abortWatcher)
- {
- abortWatcher.setown(new CWorkUnitWatcher(this, SubscribeOptionAbort, p->queryName()));
- abortDirty = true;
- }
- }
- void CPersistedWorkUnit::unsubscribe()
- {
- CriticalBlock block(crit);
- if (abortWatcher)
- {
- abortWatcher->unsubscribe();
- abortWatcher.clear();
- }
- }
- bool CPersistedWorkUnit::aborting() const
- {
- CriticalBlock block(crit);
- if (abortDirty)
- {
- StringBuffer apath;
- apath.append("/WorkUnitAborts/").append(p->queryName());
- Owned<IRemoteConnection> acon = querySDS().connect(apath.str(), myProcessSession(), 0, SDS_LOCK_TIMEOUT);
- if (acon)
- abortState = acon->queryRoot()->getPropInt(NULL) != 0;
- else
- abortState = false;
- abortDirty = false;
- }
- return abortState;
- }
- class CDaliWorkUnit : public CPersistedWorkUnit
- {
- friend class CDaliWuGraphStats;
- public:
- CDaliWorkUnit(IRemoteConnection *_conn, ISecManager *secmgr, ISecUser *secuser)
- : connection(_conn), CPersistedWorkUnit(secmgr, secuser)
- {
- loadPTree(connection->getRoot());
- }
- ~CDaliWorkUnit()
- {
- // NOTE - order is important - we need to construct connection before p and (especially) destroy after p
- // We use the beforeDispose() in base class to help ensure this
- p.clear();
- }
- IConstWUGraphProgress *getGraphProgress(const char *graphName) const
- {
- Owned<IRemoteConnection> conn = getProgressConnection();
- if (conn)
- {
- IPTree *progress = conn->queryRoot()->queryPropTree(graphName);
- if (progress)
- return new CConstGraphProgress(p->queryName(), graphName, progress);
- }
- return NULL;
- }
- virtual WUGraphState queryGraphState(const char *graphName) const
- {
- Owned<IRemoteConnection> conn = getProgressConnection();
- if (conn)
- {
- IPTree *progress = conn->queryRoot()->queryPropTree(graphName);
- if (progress)
- return (WUGraphState) progress->getPropInt("@_state", (unsigned) WUGraphUnknown);
- }
- return WUGraphUnknown;
- }
- virtual WUGraphState queryNodeState(const char *graphName, WUGraphIDType nodeId) const
- {
- Owned<IRemoteConnection> conn = getProgressConnection();
- if (conn)
- {
- IPTree *progress = conn->queryRoot()->queryPropTree(graphName);
- if (progress)
- {
- StringBuffer path;
- // NOTE - the node state info still uses the old graph layout, even when the stats are using the new...
- path.append("node[@id=\"").append(nodeId).append("\"]/@_state");
- return (WUGraphState) progress->getPropInt(path, (unsigned) WUGraphUnknown);
- }
- }
- return WUGraphUnknown;
- }
- virtual void clearGraphProgress() const
- {
- CriticalBlock block(crit);
- progressConnection.clear(); // Make sure nothing is locking for read or we won't be able to lock for write
- StringBuffer path("/GraphProgress/");
- path.append(p->queryName());
- Owned<IRemoteConnection> delconn = querySDS().connect(path.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if (delconn)
- delconn->close(true);
- }
- virtual bool getRunningGraph(IStringVal &graphName, WUGraphIDType &subId) const
- {
- Owned<IRemoteConnection> conn = getProgressConnection();
- if (!conn)
- return false;
- const char *name = conn->queryRoot()->queryProp("Running/@graph");
- if (name)
- {
- graphName.set(name);
- subId = conn->queryRoot()->getPropInt64("Running/@subId");
- return true;
- }
- else
- return false;
- }
- virtual void forceReload()
- {
- synchronized sync(locked); // protect locked workunits (uncommitted writes) from reload
- CriticalBlock block(crit);
- clearCached(true);
- connection->reload();
- progressConnection.clear();
- abortDirty = true;
- p.setown(connection->getRoot());
- }
- virtual void cleanupAndDelete(bool deldll, bool deleteOwned, const StringArray *deleteExclusions)
- {
- CPersistedWorkUnit::cleanupAndDelete(deldll, deleteOwned, deleteExclusions);
- clearGraphProgress();
- connection->close(true);
- connection.clear();
- }
- virtual void commit()
- {
- CPersistedWorkUnit::commit();
- if (connection)
- connection->commit();
- }
- virtual void _lockRemote()
- {
- StringBuffer wuRoot;
- getXPath(wuRoot, p->queryName());
- if (connection)
- connection->changeMode(RTM_LOCK_WRITE,SDS_LOCK_TIMEOUT);
- else
- connection.setown(querySDS().connect(wuRoot.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
- if (!connection)
- throw MakeStringException(WUERR_LockFailed, "Failed to get connection for xpath %s", wuRoot.str());
- clearCached(true);
- p.setown(connection->getRoot());
- }
- virtual void _unlockRemote()
- {
- if (connection)
- {
- try
- {
- try
- {
- connection->commit();
- }
- catch (IException *e)
- {
- EXCLOG(e, "Error during workunit commit");
- connection->rollback();
- connection->changeMode(0, SDS_LOCK_TIMEOUT);
- throw;
- }
- connection->changeMode(0, SDS_LOCK_TIMEOUT);
- }
- catch (IException *E)
- {
- StringBuffer s;
- IERRLOG("Failed to release write lock on workunit: %s", E->errorMessage(s).str());
- throw;
- }
- }
- }
- virtual void setGraphState(const char *graphName, unsigned wfid, WUGraphState state) const
- {
- Owned<IRemoteConnection> conn = getWritableProgressConnection(graphName, wfid);
- conn->queryRoot()->setPropInt("@_state", state);
- }
- virtual void setNodeState(const char *graphName, WUGraphIDType nodeId, WUGraphState state) const
- {
- CriticalBlock block(crit);
- progressConnection.clear(); // Make sure nothing is locking for read or we won't be able to lock for write
- VStringBuffer path("/GraphProgress/%s", queryWuid());
- Owned<IRemoteConnection> conn = querySDS().connect(path, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- IPTree *progress = ensurePTree(conn->queryRoot(), graphName);
- // NOTE - the node state info still uses the old graph layout, even when the stats are using the new...
- path.clear().append("node[@id=\"").append(nodeId).append("\"]");
- IPropertyTree *node = progress->queryPropTree(path.str());
- if (!node)
- {
- node = progress->addPropTree("node");
- node->setPropInt64("@id", nodeId);
- }
- node->setPropInt("@_state", (unsigned)state);
- switch (state)
- {
- case WUGraphRunning:
- {
- IPropertyTree *running = conn->queryRoot()->setPropTree("Running");
- running->setProp("@graph", graphName);
- running->setPropInt64("@subId", nodeId);
- break;
- }
- case WUGraphComplete:
- {
- conn->queryRoot()->removeProp("Running"); // only one thing running at any given time and one thing with lockWrite access
- break;
- }
- }
- }
- virtual IWUGraphStats *updateStats(const char *graphName, StatisticCreatorType creatorType, const char * creator, unsigned _wfid, unsigned subgraph, bool merge) const override
- {
- return new CDaliWuGraphStats(this, creatorType, creator, _wfid, graphName, subgraph, merge);
- }
- virtual void import(IPropertyTree *wuTree, IPropertyTree *graphProgressTree)
- {
- connection->queryRoot()->setPropTree(nullptr, LINK(wuTree));
- loadPTree(connection->getRoot());
- if (!graphProgressTree)
- return;
- VStringBuffer xpath("/GraphProgress/%s", queryWuid());
- Owned<IRemoteConnection> progressConn = querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE, SDS_LOCK_TIMEOUT);
- if (!progressConn)
- throw MakeStringException(0, "Failed to access %s.", xpath.str());
- progressConn->queryRoot()->setPropTree(nullptr, LINK(graphProgressTree));
- }
- protected:
- IRemoteConnection *getProgressConnection() const
- {
- CriticalBlock block(crit);
- if (!progressConnection)
- {
- VStringBuffer path("/GraphProgress/%s", queryWuid());
- progressConnection.setown(querySDS().connect(path, myProcessSession(), 0, SDS_LOCK_TIMEOUT)); // Note - we don't lock. The writes are atomic.
- }
- return progressConnection.getLink();
- }
- IRemoteConnection *getWritableProgressConnection(const char *graphName, unsigned wfid) const
- {
- CriticalBlock block(crit);
- progressConnection.clear(); // Make sure subsequent reads from this workunit get the changes I am making
- VStringBuffer path("/GraphProgress/%s/%s", queryWuid(), graphName);
- Owned<IRemoteConnection> conn = querySDS().connect(path, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- IPropertyTree * root = conn->queryRoot();
- assertex(wfid);
- if (!root->hasProp("@wfid"))
- {
- root->setPropInt("@wfid", wfid);
- }
- else
- {
- //Ideally the following code would check that the wfids are passed consistently.
- //However there is an obscure problem with out of line functions being called from multiple workflow
- //ids, and possibly library graphs.
- //Stats for library graphs should be nested below the library call activity
- //assertex(root->getPropInt("@wfid", 0) == wfid); // check that wfid is passed consistently
- }
- return conn.getClear();
- }
- IPropertyTree *getGraphProgressTree() const
- {
- Owned<IRemoteConnection> conn = getProgressConnection();
- if (conn)
- {
- Owned<IPropertyTree> tmp = createPTree("GraphProgress");
- mergePTree(tmp,conn->queryRoot());
- return tmp.getClear();
- }
- return NULL;
- }
- Owned<IRemoteConnection> connection;
- mutable Owned<IRemoteConnection> progressConnection;
- };
- class CLockedWorkUnit : implements ILocalWorkUnit, implements IExtendedWUInterface, public CInterface
- {
- public:
- Owned<CLocalWorkUnit> c;
- IMPLEMENT_IINTERFACE;
- CLockedWorkUnit(CLocalWorkUnit *_c) : c(_c) {}
- ~CLockedWorkUnit()
- {
- if (workUnitTraceLevel > 1)
- DBGLOG("Releasing locked workunit %s", queryWuid());
- if (c)
- {
- try
- {
- c->unlockRemote();
- }
- catch (IException *E)
- {
- // Exceptions here should be very uncommon - but there's also not a lot we can do if we get one
- // Allowing them to be thrown out of the destructor is going to terminate the program which is NOT what we want.
- EXCLOG(E);
- ::Release(E);
- }
- }
- }
- virtual IConstWorkUnit * unlock() override
- {
- c->unlockRemote();
- return c.getClear();
- }
- virtual bool aborting() const
- { return c->aborting(); }
- virtual void forceReload()
- { }
- virtual WUAction getAction() const
- { return c->getAction(); }
- virtual const char *queryActionDesc() const
- { return c->queryActionDesc(); }
- virtual IStringVal & getApplicationValue(const char * application, const char * propname, IStringVal & str) const
- { return c->getApplicationValue(application, propname, str); }
- virtual int getApplicationValueInt(const char * application, const char * propname, int defVal) const
- { return c->getApplicationValueInt(application, propname, defVal); }
- virtual IConstWUAppValueIterator & getApplicationValues() const
- { return c->getApplicationValues(); }
- virtual bool hasWorkflow() const
- { return c->hasWorkflow(); }
- virtual unsigned queryEventScheduledCount() const
- { return c->queryEventScheduledCount(); }
- virtual IPropertyTree * queryWorkflowTree() const
- { return c->queryWorkflowTree(); }
- virtual IConstWorkflowItemIterator * getWorkflowItems() const
- { return c->getWorkflowItems(); }
- virtual IWorkflowItemArray * getWorkflowClone() const
- { return c->getWorkflowClone(); }
- virtual bool requiresLocalFileUpload() const
- { return c->requiresLocalFileUpload(); }
- virtual IConstLocalFileUploadIterator * getLocalFileUploads() const
- { return c->getLocalFileUploads(); }
- virtual bool getIsQueryService() const
- { return c->getIsQueryService(); }
- virtual bool getCloneable() const
- { return c->getCloneable(); }
- virtual IUserDescriptor * queryUserDescriptor() const
- { return c->queryUserDescriptor(); }
- virtual const char *queryClusterName() const
- { return c->queryClusterName(); }
- virtual unsigned getCodeVersion() const
- { return c->getCodeVersion(); }
- virtual unsigned getWuidVersion() const
- { return c->getWuidVersion(); }
- virtual void getBuildVersion(IStringVal & buildVersion, IStringVal & eclVersion) const
- { c->getBuildVersion(buildVersion, eclVersion); }
- virtual bool hasDebugValue(const char * propname) const
- { return c->hasDebugValue(propname); }
- virtual IStringVal & getDebugValue(const char * propname, IStringVal & str) const
- { return c->getDebugValue(propname, str); }
- virtual int getDebugValueInt(const char * propname, int defVal) const
- { return c->getDebugValueInt(propname, defVal); }
- virtual __int64 getDebugValueInt64(const char * propname, __int64 defVal) const
- { return c->getDebugValueInt64(propname, defVal); }
- virtual double getDebugValueReal(const char * propname, double defVal) const
- { return c->getDebugValueReal(propname, defVal); }
- virtual bool getDebugValueBool(const char * propname, bool defVal) const
- { return c->getDebugValueBool(propname, defVal); }
- virtual IStringIterator & getDebugValues() const
- { return c->getDebugValues(NULL); }
- virtual IStringIterator & getDebugValues(const char *prop) const
- { return c->getDebugValues(prop); }
- virtual unsigned getExceptionCount() const
- { return c->getExceptionCount(); }
- virtual IConstWUExceptionIterator & getExceptions() const
- { return c->getExceptions(); }
- virtual unsigned getGraphCount() const
- { return c->getGraphCount(); }
- virtual unsigned getSourceFileCount() const
- { return c->getSourceFileCount(); }
- virtual unsigned getResultCount() const
- { return c->getResultCount(); }
- virtual unsigned getVariableCount() const
- { return c->getVariableCount(); }
- virtual unsigned getApplicationValueCount() const
- { return c->getApplicationValueCount(); }
- virtual IConstWUGraphIterator & getGraphs(WUGraphType type) const
- { return c->getGraphs(type); }
- virtual IConstWUGraphMetaIterator & getGraphsMeta(WUGraphType type) const
- { return c->getGraphsMeta(type); }
- virtual IConstWUGraph * getGraph(const char *name) const
- { return c->getGraph(name); }
- virtual IConstWUGraphProgress * getGraphProgress(const char * name) const
- { return c->getGraphProgress(name); }
- virtual const char *queryJobName() const
- { return c->queryJobName(); }
- virtual IConstWUPlugin * getPluginByName(const char * name) const
- { return c->getPluginByName(name); }
- virtual IConstWUPluginIterator & getPlugins() const
- { return c->getPlugins(); }
- virtual IConstWULibrary* getLibraryByName(const char *name) const
- { return c->getLibraryByName(name); }
- virtual IConstWULibraryIterator & getLibraries() const
- { return c->getLibraries(); }
- virtual WUPriorityClass getPriority() const
- { return c->getPriority(); }
- virtual const char *queryPriorityDesc() const
- { return c->queryPriorityDesc(); }
- virtual int getPriorityLevel() const
- { return c->getPriorityLevel(); }
- virtual int getPriorityValue() const
- { return c->getPriorityValue(); }
- virtual IConstWUQuery * getQuery() const
- { return c->getQuery(); }
- virtual IConstWUWebServicesInfo * getWebServicesInfo() const
- { return c->getWebServicesInfo(); }
- virtual bool getRescheduleFlag() const
- { return c->getRescheduleFlag(); }
- virtual IConstWUResult * getResultByName(const char * name) const
- { return c->getResultByName(name); }
- virtual IConstWUResult * getResultBySequence(unsigned seq) const
- { return c->getResultBySequence(seq); }
- virtual IConstWUResult * getQueryResultByName(const char * name) const
- { return c->getQueryResultByName(name); }
- virtual unsigned getResultLimit() const
- { return c->getResultLimit(); }
- virtual IConstWUResultIterator & getResults() const
- { return c->getResults(); }
- virtual IStringVal & getScope(IStringVal & str) const
- { return c->getScope(str); }
- virtual IStringVal & getWorkunitDistributedAccessToken(IStringVal & str) const
- { return c->getWorkunitDistributedAccessToken(str); }
- virtual WUState getState() const
- { return c->getState(); }
- virtual IStringVal & getStateEx(IStringVal & str) const
- { return c->getStateEx(str); }
- virtual __int64 getAgentSession() const
- { return c->getAgentSession(); }
- virtual unsigned getAgentPID() const
- { return c->getAgentPID(); }
- virtual const char *queryStateDesc() const
- { return c->queryStateDesc(); }
- virtual bool getRunningGraph(IStringVal & graphName, WUGraphIDType & subId) const
- { return c->getRunningGraph(graphName, subId); }
- virtual IConstWUScopeIterator & getScopeIterator(const WuScopeFilter & filter) const override
- { return c->getScopeIterator(filter); }
- virtual bool getStatistic(stat_type & value, const char * scope, StatisticKind kind) const override
- { return c->getStatistic(value, scope, kind); }
- virtual IStringVal & getSnapshot(IStringVal & str) const
- { return c->getSnapshot(str); }
- virtual const char *queryUser() const
- { return c->queryUser(); }
- virtual ErrorSeverity getWarningSeverity(unsigned code, ErrorSeverity defaultSeverity) const
- { return c->getWarningSeverity(code, defaultSeverity); }
- virtual const char *queryWuScope() const
- { return c->queryWuScope(); }
- virtual const char *queryWuid() const
- { return c->queryWuid(); }
- virtual IConstWUResult * getGlobalByName(const char * name) const
- { return c->getGlobalByName(name); }
- virtual IConstWUResult * getTemporaryByName(const char * name) const
- { return c->getTemporaryByName(name); }
- virtual IConstWUResultIterator & getTemporaries() const
- { return c->getTemporaries(); }
- virtual IConstWUResult * getVariableByName(const char * name) const
- { return c->getVariableByName(name); }
- virtual IConstWUResultIterator & getVariables() const
- { return c->getVariables(); }
- virtual bool isProtected() const
- { return c->isProtected(); }
- virtual bool isPausing() const
- { return c->isPausing(); }
- virtual IWorkUnit & lock()
- { ((CInterface *)this)->Link(); return (IWorkUnit &) *this; }
- virtual bool reload()
- { UNIMPLEMENTED; }
- virtual void subscribe(WUSubscribeOptions options)
- { c->subscribe(options); }
- virtual void requestAbort()
- { c->requestAbort(); }
- virtual unsigned calculateHash(unsigned prevHash)
- { return queryExtendedWU(c)->calculateHash(prevHash); }
- virtual void copyWorkUnit(IConstWorkUnit *cached, bool copyStats, bool all)
- { queryExtendedWU(c)->copyWorkUnit(cached, copyStats, all); }
- virtual IPropertyTree *queryPTree() const
- { return queryExtendedWU(c)->queryPTree(); }
- virtual IPropertyTree *getUnpackedTree(bool includeProgress) const
- { return queryExtendedWU(c)->getUnpackedTree(includeProgress); }
- virtual bool archiveWorkUnit(const char *base,bool del,bool deldll,bool deleteOwned,bool exportAssociatedFiles)
- { return queryExtendedWU(c)->archiveWorkUnit(base,del,deldll,deleteOwned,exportAssociatedFiles); }
- virtual unsigned queryFileUsage(const char *filename) const
- { return c->queryFileUsage(filename); }
- virtual IConstWUFileUsageIterator * getFieldUsage() const
- { return c->getFieldUsage(); }
- virtual bool getFieldUsageArray(StringArray & filenames, StringArray & columnnames, const char * clusterName) const
- { return c->getFieldUsageArray(filenames, columnnames, clusterName); }
- virtual IJlibDateTime & getTimeScheduled(IJlibDateTime &val) const
- { return c->getTimeScheduled(val); }
- virtual unsigned getDebugAgentListenerPort() const
- { return c->getDebugAgentListenerPort(); }
- virtual IStringVal & getDebugAgentListenerIP(IStringVal &ip) const
- { return c->getDebugAgentListenerIP(ip); }
- virtual IStringVal & getXmlParams(IStringVal & params, bool hidePasswords) const
- { return c->getXmlParams(params, hidePasswords); }
- virtual const IPropertyTree *getXmlParams() const
- { return c->getXmlParams(); }
- virtual unsigned __int64 getHash() const
- { return c->getHash(); }
- virtual IStringIterator *getLogs(const char *type, const char *instance) const
- { return c->getLogs(type, instance); }
- virtual IStringIterator *getProcesses(const char *type) const
- { return c->getProcesses(type); }
- virtual IPropertyTreeIterator* getProcesses(const char *type, const char *instance) const
- { return c->getProcesses(type, instance); }
- virtual unsigned getTotalThorTime() const
- { return c->getTotalThorTime(); }
- virtual WUGraphState queryGraphState(const char *graphName) const
- { return c->queryGraphState(graphName); }
- virtual WUGraphState queryNodeState(const char *graphName, WUGraphIDType nodeId) const
- { return c->queryNodeState(graphName, nodeId); }
- virtual void setGraphState(const char *graphName, unsigned wfid, WUGraphState state) const
- { c->setGraphState(graphName, wfid, state); }
- virtual void setNodeState(const char *graphName, WUGraphIDType nodeId, WUGraphState state) const
- { c->setNodeState(graphName, nodeId, state); }
- virtual IWUGraphStats *updateStats(const char *graphName, StatisticCreatorType creatorType, const char * creator, unsigned _wfid, unsigned subgraph, bool merge) const override
- { return c->updateStats(graphName, creatorType, creator, _wfid, subgraph, merge); }
- virtual void clearGraphProgress() const
- { c->clearGraphProgress(); }
- virtual IStringVal & getAbortBy(IStringVal & str) const
- { return c->getAbortBy(str); }
- virtual unsigned __int64 getAbortTimeStamp() const
- { return c->getAbortTimeStamp(); }
- virtual cost_type getExecuteCost() const
- { return c->getExecuteCost(); }
- virtual cost_type getFileAccessCost() const
- { return c->getFileAccessCost(); }
- virtual void import(IPropertyTree *wuTree, IPropertyTree *graphProgressTree)
- { return c->import(wuTree, graphProgressTree); }
- virtual void clearExceptions(const char *source=nullptr)
- { c->clearExceptions(source); }
- virtual void commit()
- { c->commit(); }
- virtual IWUException * createException()
- { return c->createException(); }
- virtual void addProcess(const char *type, const char *instance, unsigned pid, unsigned max, const char *pattern, bool singleLog, const char *log)
- { c->addProcess(type, instance, pid, max, pattern, singleLog, log); }
- virtual void protect(bool protectMode)
- { c->protect(protectMode); }
- virtual void setAction(WUAction action)
- { c->setAction(action); }
- virtual void setApplicationValue(const char * application, const char * propname, const char * value, bool overwrite)
- { c->setApplicationValue(application, propname, value, overwrite); }
- virtual void setApplicationValueInt(const char * application, const char * propname, int value, bool overwrite)
- { c->setApplicationValueInt(application, propname, value, overwrite); }
- virtual void incEventScheduledCount()
- { c->incEventScheduledCount(); }
- virtual void setIsQueryService(bool value)
- { c->setIsQueryService(value); }
- virtual void setCloneable(bool value)
- { c->setCloneable(value); }
- virtual void setIsClone(bool value)
- { c->setIsClone(value); }
- virtual void setClusterName(const char * value)
- { c->setClusterName(value); }
- virtual void setCodeVersion(unsigned version, const char * buildVersion, const char * eclVersion)
- { c->setCodeVersion(version, buildVersion, eclVersion); }
- virtual void setDebugValue(const char * propname, const char * value, bool overwrite)
- { c->setDebugValue(propname, value, overwrite); }
- virtual void setDebugValueInt(const char * propname, int value, bool overwrite)
- { c->setDebugValueInt(propname, value, overwrite); }
- virtual void setJobName(const char * value)
- { c->setJobName(value); }
- virtual void setPriority(WUPriorityClass cls)
- { c->setPriority(cls); }
- virtual void setPriorityLevel(int level)
- { c->setPriorityLevel(level); }
- virtual void setRescheduleFlag(bool value)
- { c->setRescheduleFlag(value); }
- virtual void setResultLimit(unsigned value)
- { c->setResultLimit(value); }
- virtual void setState(WUState state)
- { c->setState(state); }
- virtual void setStateEx(const char * text)
- { c->setStateEx(text); }
- virtual void setAgentSession(__int64 sessionId)
- { c->setAgentSession(sessionId); }
- 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)
- { c->setStatistic(creatorType, creator, scopeType, scope, kind, optDescription, value, count, maxValue, mergeAction); }
- virtual void setTracingValue(const char * propname, const char * value)
- { c->setTracingValue(propname, value); }
- virtual void setTracingValueInt(const char * propname, int value)
- { c->setTracingValueInt(propname, value); }
- virtual void setTracingValueInt64(const char * propname, __int64 value)
- { c->setTracingValueInt64(propname, value); }
- virtual void setUser(const char * value)
- { c->setUser(value); }
- virtual void setWuScope(const char * value)
- { c->setWuScope(value); }
- virtual IWorkflowItem* addWorkflowItem(unsigned wfid, WFType type, WFMode mode, unsigned success, unsigned failure, unsigned recovery, unsigned retriesAllowed, unsigned contingencyFor)
- { return c->addWorkflowItem(wfid, type, mode, success, failure, recovery, retriesAllowed, contingencyFor); }
- virtual void syncRuntimeWorkflow(IWorkflowItemArray * array)
- { c->syncRuntimeWorkflow(array); }
- virtual IWorkflowItemIterator * updateWorkflowItems()
- { return c->updateWorkflowItems(); }
- virtual void resetWorkflow()
- { c->resetWorkflow(); }
- virtual void schedule()
- { c->schedule(); }
- virtual void deschedule()
- { c->deschedule(); }
- virtual unsigned addLocalFileUpload(LocalFileUploadType type, char const * source, char const * destination, char const * eventTag)
- { return c->addLocalFileUpload(type, source, destination, eventTag); }
- virtual IWUResult * updateGlobalByName(const char * name)
- { return c->updateGlobalByName(name); }
- virtual void createGraph(const char * name, const char *label, WUGraphType type, IPropertyTree *xgmml, unsigned wfid)
- { c->createGraph(name, label, type, xgmml, wfid); }
- virtual IWUQuery * updateQuery()
- { return c->updateQuery(); }
- virtual IWUWebServicesInfo * updateWebServicesInfo(bool create)
- { return c->updateWebServicesInfo(create); }
- virtual IWUPlugin * updatePluginByName(const char * name)
- { return c->updatePluginByName(name); }
- virtual IWULibrary * updateLibraryByName(const char * name)
- { return c->updateLibraryByName(name); }
- virtual IWUResult * updateResultByName(const char * name)
- { return c->updateResultByName(name); }
- virtual IWUResult * updateResultBySequence(unsigned seq)
- { return c->updateResultBySequence(seq); }
- virtual IWUResult * updateTemporaryByName(const char * name)
- { return c->updateTemporaryByName(name); }
- virtual IWUResult * updateVariableByName(const char * name)
- { return c->updateVariableByName(name); }
- virtual void addFile(const char *fileName, StringArray *clusters, unsigned usageCount, WUFileKind fileKind, const char *graphOwner)
- { c->addFile(fileName, clusters, usageCount, fileKind, graphOwner); }
- virtual void noteFileRead(IDistributedFile *file)
- { c->noteFileRead(file); }
- virtual void noteFieldUsage(IPropertyTree * usage)
- { c->noteFieldUsage(usage); }
- virtual void releaseFile(const char *fileName)
- { c->releaseFile(fileName); }
- virtual void resetBeforeGeneration()
- { c->resetBeforeGeneration(); }
- virtual void deleteTempFiles(const char *graph, bool deleteOwned, bool deleteJobOwned)
- { c->deleteTempFiles(graph, deleteOwned, deleteJobOwned); }
- virtual void deleteTemporaries()
- { c->deleteTemporaries(); }
- virtual void addDiskUsageStats(__int64 avgNodeUsage, unsigned minNode, __int64 minNodeUsage, unsigned maxNode, __int64 maxNodeUsage, __int64 graphId)
- { c->addDiskUsageStats(avgNodeUsage, minNode, minNodeUsage, maxNode, maxNodeUsage, graphId); }
- virtual IPropertyTree * getDiskUsageStats()
- { return c->getDiskUsageStats(); }
- virtual IPropertyTreeIterator & getFileIterator() const
- { return c->getFileIterator(); }
- virtual IPropertyTreeIterator & getFilesReadIterator() const
- { return c->getFilesReadIterator(); }
- virtual void setSnapshot(const char * value)
- { c->setSnapshot(value); }
- virtual void setWarningSeverity(unsigned code, ErrorSeverity severity)
- { c->setWarningSeverity(code, severity); }
- virtual void setTimeScheduled(const IJlibDateTime &val)
- { c->setTimeScheduled(val); }
- virtual void setDebugAgentListenerPort(unsigned port)
- { c->setDebugAgentListenerPort(port); }
- virtual void setDebugAgentListenerIP(const char * ip)
- { c->setDebugAgentListenerIP(ip); }
- virtual void setXmlParams(const char *params)
- { c->setXmlParams(params); }
- virtual void setXmlParams(IPropertyTree *tree)
- { c->setXmlParams(tree); }
- virtual void setHash(unsigned __int64 hash)
- { c->setHash(hash); }
- // ILocalWorkUnit - used for debugging etc
- virtual void serialize(MemoryBuffer &tgt)
- { c->serialize(tgt); }
- virtual void deserialize(MemoryBuffer &src)
- { c->deserialize(src); }
- virtual bool switchThorQueue(const char *cluster, IQueueSwitcher *qs)
- { return c->switchThorQueue(cluster,qs); }
- virtual void setAllowedClusters(const char *value)
- { c->setAllowedClusters(value); }
- virtual IStringVal& getAllowedClusters(IStringVal &str) const
- { return c->getAllowedClusters(str); }
- virtual void remoteCheckAccess(IUserDescriptor *user, bool writeaccess) const
- { c->remoteCheckAccess(user,writeaccess); }
- virtual void setAllowAutoQueueSwitch(bool val)
- { c->setAllowAutoQueueSwitch(val); }
- virtual bool getAllowAutoQueueSwitch() const
- { return c->getAllowAutoQueueSwitch(); }
- virtual void setLibraryInformation(const char * name, unsigned interfaceHash, unsigned definitionHash)
- { c->setLibraryInformation(name, interfaceHash, definitionHash); }
- virtual void setResultInt(const char * name, unsigned sequence, __int64 val)
- { c->setResultInt(name, sequence, val); }
- virtual void setResultUInt(const char * name, unsigned sequence, unsigned __int64 val)
- { c->setResultUInt(name, sequence, val); }
- virtual void setResultReal(const char *name, unsigned sequence, double val)
- { c->setResultReal(name, sequence, val); }
- virtual void setResultVarString(const char * stepname, unsigned sequence, const char *val)
- { c->setResultVarString(stepname, sequence, val); }
- virtual void setResultVarUnicode(const char * stepname, unsigned sequence, UChar const *val)
- { c->setResultVarUnicode(stepname, sequence, val); }
- virtual void setResultString(const char * stepname, unsigned sequence, int len, const char *val)
- { c->setResultString(stepname, sequence, len, val); }
- virtual void setResultData(const char * stepname, unsigned sequence, int len, const void *val)
- { c->setResultData(stepname, sequence, len, val); }
- virtual void setResultRaw(const char * name, unsigned sequence, int len, const void *val)
- { c->setResultRaw(name, sequence, len, val); }
- virtual void setResultSet(const char * name, unsigned sequence, bool isAll, size32_t len, const void *val, ISetToXmlTransformer *xform)
- { c->setResultSet(name, sequence, isAll, len, val, xform); }
- virtual void setResultUnicode(const char * name, unsigned sequence, int len, UChar const * val)
- { c->setResultUnicode(name, sequence, len, val); }
- virtual void setResultBool(const char *name, unsigned sequence, bool val)
- { c->setResultBool(name, sequence, val); }
- virtual void setResultDecimal(const char *name, unsigned sequence, int len, int precision, bool isSigned, const void *val)
- { c->setResultDecimal(name, sequence, len, precision, isSigned, val); }
- virtual void setResultDataset(const char * name, unsigned sequence, size32_t len, const void *val, unsigned numRows, bool extend)
- { c->setResultDataset(name, sequence, len, val, numRows, extend); }
- };
- IPropertyTree &CDaliWuGraphStats::queryProgressTree()
- {
- conn.setown(owner->getWritableProgressConnection(graphName, wfid));
- return *conn->queryRoot();
- }
- class CLocalWUAssociated : implements IConstWUAssociatedFile, public CInterface
- {
- Owned<IPropertyTree> p;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUAssociated(IPropertyTree *p);
- virtual WUFileType getType() const;
- virtual IStringVal & getDescription(IStringVal & ret) const;
- virtual IStringVal & getIp(IStringVal & ret) const;
- virtual IStringVal & getName(IStringVal & ret) const;
- virtual IStringVal & getNameTail(IStringVal & ret) const;
- virtual unsigned getCrc() const;
- virtual unsigned getMinActivityId() const;
- virtual unsigned getMaxActivityId() const;
- };
- class CLocalWUQuery : implements IWUQuery, public CInterface
- {
- Owned<IPropertyTree> p;
- mutable IArrayOf<IConstWUAssociatedFile> associated;
- mutable CriticalSection crit;
- mutable bool associatedCached;
- private:
- void addSpecialCaseAssociated(WUFileType type, const char * propname, unsigned crc) const;
- void loadAssociated() const;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUQuery(IPropertyTree *p);
- virtual WUQueryType getQueryType() const;
- virtual IStringVal& getQueryText(IStringVal &str) const;
- virtual IStringVal& getQueryShortText(IStringVal &str) const;
- virtual IStringVal& getQueryName(IStringVal &str) const;
- virtual IStringVal & getQueryMainDefinition(IStringVal & str) const;
- virtual IStringVal& getQueryDllName(IStringVal &str) const;
- virtual unsigned getQueryDllCrc() const;
- virtual IStringVal& getQueryCppName(IStringVal &str) const;
- virtual IStringVal& getQueryResTxtName(IStringVal &str) const;
- virtual IConstWUAssociatedFile * getAssociatedFile(WUFileType type, unsigned index) const;
- virtual IConstWUAssociatedFileIterator& getAssociatedFiles() const;
- virtual bool isArchive() const;
- virtual bool hasArchive() const
- {
- return p->getPropBool("@hasArchive");
- }
- virtual void setQueryType(WUQueryType qt);
- virtual void setQueryText(const char *pstr);
- virtual void setQueryName(const char *);
- virtual void setQueryMainDefinition(const char * str);
- virtual void addAssociatedFile(WUFileType type, const char * name, const char * ip, const char * desc, unsigned crc, unsigned minActivity, unsigned maxActivity);
- virtual void removeAssociatedFiles();
- virtual void removeAssociatedFile(WUFileType type, const char * name, const char * desc);
- };
- class CLocalWUWebServicesInfo : implements IWUWebServicesInfo, public CInterface
- {
- Owned<IPropertyTree> p;
- mutable CriticalSection crit;
- private:
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUWebServicesInfo(IPropertyTree *p);
- virtual IStringVal& getModuleName(IStringVal &str) const;
- virtual IStringVal& getAttributeName(IStringVal &str) const;
- virtual IStringVal& getDefaultName(IStringVal &str) const;
- virtual IStringVal& getInfo(const char *name, IStringVal &str) const;
- virtual IStringVal& getText(const char *name, IStringVal &str) const;
- virtual unsigned getWebServicesCRC() const;
- virtual void setModuleName(const char *);
- virtual void setAttributeName(const char *);
- virtual void setDefaultName(const char *);
- virtual void setInfo(const char *name, const char *info);
- virtual void setText(const char *name, const char *info);
- virtual void setWebServicesCRC(unsigned);
- };
- class CLocalWUResult : implements IWUResult, public CInterface
- {
- friend class CLocalWorkUnit;
- mutable CriticalSection crit;
- Owned<IPropertyTree> p;
- Owned<IProperties> xmlns;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUResult(IPropertyTree *props);
- ~CLocalWUResult() { try { p.clear(); } catch (IException *E) {E->Release();}}
- virtual WUResultStatus getResultStatus() const;
- virtual IStringVal& getResultName(IStringVal &str) const;
- virtual int getResultSequence() const;
- virtual bool isResultScalar() const;
- virtual IStringVal& getResultXml(IStringVal &str, bool hidePasswords) const;
- virtual unsigned getResultFetchSize() const;
- virtual __int64 getResultTotalRowCount() const;
- virtual __int64 getResultRowCount() const;
- virtual void getResultDataset(IStringVal & ecl, IStringVal & defs) const;
- virtual IStringVal& getResultLogicalName(IStringVal &ecl) const;
- virtual IStringVal& getResultKeyField(IStringVal& ecl) const;
- virtual unsigned getResultRequestedRows() const;
- virtual __int64 getResultInt() const;
- virtual bool getResultBool() const;
- virtual double getResultReal() const;
- virtual IStringVal& getResultString(IStringVal & str, bool hidePassword) const;
- virtual IDataVal& getResultRaw(IDataVal & data, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const;
- virtual IDataVal& getResultUnicode(IDataVal & data) const;
- virtual void getResultDecimal(void * val, unsigned length, unsigned precision, bool isSigned) const;
- virtual IStringVal& getResultEclSchema(IStringVal & str) const;
- virtual __int64 getResultRawSize(IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const;
- virtual IDataVal& getResultRaw(IDataVal & data, __int64 from, __int64 length, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const;
- virtual IStringVal& getResultRecordSizeEntry(IStringVal & str) const;
- virtual IStringVal& getResultTransformerEntry(IStringVal & str) const;
- virtual __int64 getResultRowLimit() const;
- virtual IStringVal& getResultFilename(IStringVal & str) const;
- virtual WUResultFormat getResultFormat() const;
- virtual unsigned getResultHash() const;
- virtual bool getResultIsAll() const;
- virtual IProperties *queryResultXmlns();
- virtual IStringVal& getResultFieldOpt(const char *name, IStringVal &str) const;
- virtual void getSchema(IArrayOf<ITypeInfo> &types, StringAttrArray &names, IStringVal * ecl=NULL) const;
- virtual void getResultWriteLocation(IStringVal & _graph, unsigned & _activityId) const;
- // interface IWUResult
- virtual void setResultStatus(WUResultStatus status);
- virtual void setResultName(const char *name);
- virtual void setResultSequence(unsigned seq);
- virtual void setResultSchemaRaw(unsigned len, const void *schema);
- virtual void setResultScalar(bool isScalar);
- virtual void setResultRaw(unsigned len, const void *xml, WUResultFormat format);
- virtual void setResultFetchSize(unsigned rows); // 0 means file-loaded
- virtual void setResultTotalRowCount(__int64 rows); // -1 means unknown
- virtual void setResultRowCount(__int64 rows);
- virtual void setResultDataset(const char *ecl, const char *defs);
- virtual void setResultLogicalName(const char *logicalName);
- virtual void setResultKeyField(const char * name);
- virtual void setResultRequestedRows(unsigned req);
- virtual void setResultRecordSizeEntry(const char * val);
- virtual void setResultTransformerEntry(const char * val);
- virtual void setResultInt(__int64 val);
- virtual void setResultReal(double val);
- virtual void setResultBool(bool val);
- virtual void setResultString(const char * val, unsigned length);
- virtual void setResultUnicode(const void * val, unsigned length);
- virtual void setResultData(const void * val, unsigned length);
- virtual void setResultDecimal(const void * val, unsigned length);
- virtual void addResultRaw(unsigned len, const void * data, WUResultFormat format);
- virtual void setResultRowLimit(__int64 value);
- virtual void setResultFilename(const char * name);
- virtual void setResultUInt(unsigned __int64 val);
- virtual void setResultIsAll(bool value);
- virtual void setResultFormat(WUResultFormat format);
- virtual void setResultXML(const char *val);
- virtual void setResultRow(unsigned len, const void * data);
- virtual void setResultXmlns(const char *prefix, const char *uri);
- virtual void setResultFieldOpt(const char *name, const char *value);
- virtual void setResultWriteLocation(const char * _graph, unsigned _activityId);
- virtual IPropertyTree *queryPTree() { return p; }
- };
- class CLocalWUPlugin : implements IWUPlugin, public CInterface
- {
- Owned<IPropertyTree> p;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUPlugin(IPropertyTree *p);
- virtual IStringVal& getPluginName(IStringVal &str) const;
- virtual IStringVal& getPluginVersion(IStringVal &str) const;
- virtual void setPluginName(const char *str);
- virtual void setPluginVersion(const char *str);
- };
- class CLocalWULibrary : implements IWULibrary, public CInterface
- {
- Owned<IPropertyTree> p;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWULibrary(IPropertyTree *p);
- virtual IStringVal & getName(IStringVal & str) const;
- virtual void setName(const char * str);
- };
- class CLocalWUException : implements IWUException, public CInterface
- {
- Owned<IPropertyTree> p;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUException(IPropertyTree *p);
- virtual IStringVal& getExceptionSource(IStringVal &str) const override;
- virtual IStringVal& getExceptionMessage(IStringVal &str) const override;
- virtual unsigned getExceptionCode() const override;
- virtual ErrorSeverity getSeverity() const override;
- virtual IStringVal & getTimeStamp(IStringVal & dt) const override;
- virtual IStringVal & getExceptionFileName(IStringVal & str) const override;
- virtual unsigned getExceptionLineNo() const override;
- virtual unsigned getExceptionColumn() const override;
- virtual unsigned getActivityId() const override;
- virtual unsigned getSequence() const override;
- virtual const char * queryScope() const override;
- virtual unsigned getPriority() const override;
- virtual void setExceptionSource(const char *str) override;
- virtual void setExceptionMessage(const char *str) override;
- virtual void setExceptionCode(unsigned code) override;
- virtual void setSeverity(ErrorSeverity level) override;
- virtual void setTimeStamp(const char * dt) override;
- virtual void setExceptionFileName(const char *str) override;
- virtual void setExceptionLineNo(unsigned r) override;
- virtual void setExceptionColumn(unsigned c) override;
- virtual void setActivityId(unsigned _id) override;
- virtual void setScope(const char * _scope) override;
- virtual void setPriority(unsigned _priority) override;
- };
- //==========================================================================================
- extern WORKUNIT_API bool isSpecialResultSequence(unsigned sequence)
- {
- switch ((int) sequence)
- {
- case ResultSequenceInternal:
- case ResultSequenceOnce:
- case ResultSequencePersist:
- case ResultSequenceStored:
- return true;
- default:
- assertex(sequence <= INT_MAX);
- if ((int) sequence >= LibraryBaseSequence)
- return true;
- return false;
- }
- }
- class CConstWUArrayIterator : implements IConstWorkUnitIterator, public CInterface
- {
- unsigned curTreeNum;
- IArrayOf<IPropertyTree> trees;
- Owned<IConstWorkUnitInfo> cur;
- void setCurrent()
- {
- cur.setown(new CLightweightWorkunitInfo(trees.item(curTreeNum)));
- }
- public:
- IMPLEMENT_IINTERFACE;
- CConstWUArrayIterator(IArrayOf<IPropertyTree> &_trees)
- {
- ForEachItemIn(t, _trees)
- trees.append(*LINK(&_trees.item(t)));
- curTreeNum = 0;
- }
- bool first()
- {
- curTreeNum = 0;
- return next();
- }
- bool isValid()
- {
- return (NULL != cur.get());
- }
- bool next()
- {
- if (curTreeNum >= trees.ordinality())
- {
- cur.clear();
- return false;
- }
- setCurrent();
- ++curTreeNum;
- return true;
- }
- IConstWorkUnitInfo & query() { return *cur; }
- };
- class CLocalWUFieldUsage : public CInterface, implements IConstWUFieldUsage
- {
- Owned<IPropertyTree> p;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUFieldUsage(IPropertyTree& _p) { p.setown(&_p); }
- virtual const char * queryName() const { return p->queryProp("@name"); }
- };
- class CConstWUFieldUsageIterator : public CInterface, implements IConstWUFieldUsageIterator
- {
- public:
- IMPLEMENT_IINTERFACE;
- CConstWUFieldUsageIterator(IPropertyTreeIterator * tree) { iter.setown(tree); }
- bool first() override { return iter->first(); }
- bool isValid() override { return iter->isValid(); }
- bool next() override { return iter->next(); }
- IConstWUFieldUsage * get() const override { return new CLocalWUFieldUsage(iter->get()); }
- private:
- Owned<IPropertyTreeIterator> iter;
- };
- class CLocalWUFileUsage : public CInterface, implements IConstWUFileUsage
- {
- Owned<IPropertyTree> p;
- public:
- IMPLEMENT_IINTERFACE;
- CLocalWUFileUsage(IPropertyTree& _p) { p.setown(&_p); }
- virtual const char * queryName() const { return p->queryProp("@name"); }
- virtual const char * queryType() const { return p->queryProp("@type"); }
- virtual unsigned getNumFields() const { return p->getPropInt("@numFields"); }
- virtual unsigned getNumFieldsUsed() const { return p->getPropInt("@numFieldsUsed"); }
- virtual IConstWUFieldUsageIterator * getFields() const { return new CConstWUFieldUsageIterator(p->getElements("fields/field")); }
- };
- class CConstWUFileUsageIterator : public CInterface, implements IConstWUFileUsageIterator
- {
- public:
- IMPLEMENT_IINTERFACE;
- CConstWUFileUsageIterator(IPropertyTreeIterator * tree) { iter.setown(tree); }
- bool first() override { return iter->first(); }
- bool isValid() override { return iter->isValid(); }
- bool next() override { return iter->next(); }
- IConstWUFileUsage * get() const override { return new CLocalWUFileUsage(iter->get()); }
- private:
- Owned<IPropertyTreeIterator> iter;
- };
- //==========================================================================================
- class CCachedJobNameIterator : implements IStringIterator, public CInterface
- {
- Owned<IPropertyTreeIterator> it;
- public:
- IMPLEMENT_IINTERFACE;
- CCachedJobNameIterator(IPropertyTreeIterator *p) : it(p) {};
- virtual bool first() { return it->first(); }
- virtual bool next() { return it->next(); }
- virtual bool isValid() { return it->isValid(); }
- virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryName()+1); return s; }
- };
- EnumMapping workunitSortFields[] =
- {
- { WUSFuser, "@submitID" },
- { WUSFcluster, "@clusterName" },
- { WUSFjob, "@jobName" },
- { WUSFstate, "@state" },
- { WUSFpriority, "@priorityClass" },
- { WUSFprotected, "@protected" },
- { WUSFwuid, "@" },
- { WUSFecl, "Query/ShortText" },
- { WUSFfileread, "FilesRead/File/@name" },
- { WUSFtotalthortime, "@totalThorTime" },
- { WUSFwuidhigh, "@" },
- { WUSFwildwuid, "@" },
- { WUSFappvalue, "Application" },
- { WUSFfilewritten, "Files/File/@name" },
- { WUSFterm, NULL }
- };
- extern const char *queryFilterXPath(WUSortField field)
- {
- return getEnumText(field, workunitSortFields);
- }
- EnumMapping querySortFields[] =
- {
- { WUQSFId, "@id" },
- { WUQSFwuid, "@wuid" },
- { WUQSFname, "@name" },
- { WUQSFdll, "@dll" },
- { WUQSFmemoryLimit, "@memoryLimit" },
- { WUQSFmemoryLimitHi, "@memoryLimit" },
- { WUQSFtimeLimit, "@timeLimit" },
- { WUQSFtimeLimitHi, "@timeLimit" },
- { WUQSFwarnTimeLimit, "@warnTimeLimit" },
- { WUQSFwarnTimeLimitHi, "@warnTimeLimit" },
- { WUQSFpriority, "@priority" },
- { WUQSFpriorityHi, "@priority" },
- { WUQSFQuerySet, "@querySetId" },
- { WUQSFActivited, "@activated" },
- { WUQSFSuspendedByUser, "@suspended" },
- { WUQSFLibrary, "Library"},
- { WUQSFPublishedBy, "@publishedBy" },
- { WUQSFterm, NULL }
- };
- class asyncRemoveDllWorkItem: public CInterface, implements IWorkQueueItem // class only used in asyncRemoveDll
- {
- StringAttr name;
- public:
- IMPLEMENT_IINTERFACE;
- asyncRemoveDllWorkItem(const char * _name) : name(_name)
- {
- }
- void execute()
- {
- PROGLOG("WU removeDll %s", name.get());
- queryDllServer().removeDll(name, true, true); // <name>, removeDlls=true, removeDirectory=true
- }
- };
- class asyncRemoveRemoteFileWorkItem: public CInterface, implements IWorkQueueItem // class only used in asyncRemoveFile
- {
- RemoteFilename name;
- public:
- IMPLEMENT_IINTERFACE;
- asyncRemoveRemoteFileWorkItem(const char * _ip, const char * _name)
- {
- SocketEndpoint ep(_ip);
- name.setPath(ep, _name);
- }
- void execute()
- {
- Owned<IFile> file = createIFile(name);
- PROGLOG("WU removeDll %s",file->queryFilename());
- file->remove();
- }
- };
- //==========================================================================================
- class CConstQuerySetQueryIterator : implements IConstQuerySetQueryIterator, public CInterface
- {
- unsigned index;
- IArrayOf<IPropertyTree> trees;
- public:
- IMPLEMENT_IINTERFACE;
- CConstQuerySetQueryIterator(IArrayOf<IPropertyTree> &_trees)
- {
- ForEachItemIn(t, _trees)
- trees.append(*LINK(&_trees.item(t)));
- index = 0;
- }
- ~CConstQuerySetQueryIterator()
- {
- trees.kill();
- }
- bool first()
- {
- index = 0;
- return (trees.ordinality()!=0);
- }
- bool next()
- {
- index++;
- return (index<trees.ordinality());
- }
- bool isValid()
- {
- return (index<trees.ordinality());
- }
- IPropertyTree &query()
- {
- return trees.item(index);
- }
- };
- class CSecurityCache
- {
- };
- class CConstWUIterator : implements IConstWorkUnitIterator, public CInterface
- {
- public:
- IMPLEMENT_IINTERFACE;
- CConstWUIterator(IPropertyTreeIterator *_ptreeIter)
- : ptreeIter(_ptreeIter)
- {
- }
- bool first()
- {
- if (!ptreeIter->first())
- {
- cur.clear();
- return false;
- }
- cur.setown(new CLightweightWorkunitInfo(ptreeIter->query()));
- return true;
- }
- bool isValid()
- {
- return (NULL != cur.get());
- }
- bool next()
- {
- if (!ptreeIter->next())
- {
- cur.clear();
- return false;
- }
- cur.setown(new CLightweightWorkunitInfo(ptreeIter->query()));
- return true;
- }
- IConstWorkUnitInfo & query() { return *cur; }
- private:
- Owned<IConstWorkUnitInfo> cur;
- Owned<IPropertyTreeIterator> ptreeIter;
- };
- class CSecureConstWUIterator : public CInterfaceOf<IConstWorkUnitIterator>
- {
- public:
- CSecureConstWUIterator(IConstWorkUnitIterator *_parent, ISecManager *_secmgr=NULL, ISecUser *_secuser=NULL)
- : parent(_parent), secmgr(_secmgr), secuser(_secuser)
- {
- assertex(_secuser && _secmgr);
- }
- bool first()
- {
- if (!parent->first())
- return false;
- return getNext();
- }
- bool next()
- {
- if (!parent->next())
- return false;
- return getNext();
- }
- virtual bool isValid()
- {
- return parent->isValid();
- }
- virtual IConstWorkUnitInfo &query()
- {
- return parent->query();
- }
- private:
- Owned<IConstWorkUnitIterator> parent;
- MapStringTo<int> scopePermissions;
- Linked<ISecManager> secmgr;
- Linked<ISecUser> secuser;
- bool getNext() // scan for a workunit with permissions
- {
- do
- {
- const char *scopeName = parent->query().queryWuScope();
- if (!scopeName || !*scopeName || checkScope(scopeName))
- return true;
- } while (parent->next());
- return false;
- }
- bool checkScope(const char *scopeName)
- {
- int *perms = scopePermissions.getValue(scopeName);
- SecAccessFlags perm;
- if (!perms)
- {
- perm = secuser.get() ? secmgr->authorizeWorkunitScope(*secuser, scopeName) : SecAccess_Unavailable;
- scopePermissions.setValue(scopeName, perm);
- }
- else
- perm = (SecAccessFlags)*perms;
- return perm >= SecAccess_Read;
- }
- };
- CWorkUnitFactory::CWorkUnitFactory()
- {
- }
- CWorkUnitFactory::~CWorkUnitFactory()
- {
- }
- IWorkUnit* CWorkUnitFactory::createNamedWorkUnit(const char *wuid, const char *app, const char *scope, ISecManager *secmgr, ISecUser *secuser)
- {
- checkWuScopeSecAccess(scope, secmgr, secuser, SecAccess_Write, "Create", true, true);
- Owned<CLocalWorkUnit> cw = _createWorkUnit(wuid, secmgr, secuser);
- if (scope)
- 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...
- cw->setDistributedAccessToken(secuser ? secuser->getName() : "");//create and sign the workunit distributed access token
- IWorkUnit* ret = &cw->lockRemote(false); // Note - this may throw exception if user does not have rights.
- ret->setDebugValue("CREATED_BY", app, true);
- ret->setDebugValue("CREATED_FOR", scope, true);
- return ret;
- }
- IWorkUnit* CWorkUnitFactory::createWorkUnit(const char *app, const char *scope, ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer wuid("W");
- char result[32];
- time_t ltime;
- time( <ime );
- tm *today = localtime( <ime ); // MORE - this is not threadsafe. But I probably don't care that much!
- strftime(result, sizeof(result), "%Y%m%d-%H%M%S", today);
- wuid.append(result);
- if (workUnitTraceLevel > 1)
- DBGLOG("createWorkUnit created %s", wuid.str());
- IWorkUnit* ret = createNamedWorkUnit(wuid.str(), app, scope, secmgr, secuser);
- if (workUnitTraceLevel > 1)
- DBGLOG("createWorkUnit created %s", ret->queryWuid());
- addTimeStamp(ret, SSTglobal, NULL, StWhenCreated);
- return ret;
- }
- bool CWorkUnitFactory::deleteWorkUnitEx(const char * wuid, bool throwException, ISecManager *secmgr, ISecUser *secuser)
- {
- if (workUnitTraceLevel > 1)
- DBGLOG("deleteWorkUnit %s", wuid);
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- Owned<CLocalWorkUnit> cw = _updateWorkUnit(wuid, secmgr, secuser);
- if (!checkWuSecAccess(*cw.get(), secmgr, secuser, SecAccess_Full, "delete", true, true))
- return false;
- if (throwException)
- cw->cleanupAndDelete(true, true);
- else
- {
- try
- {
- cw->cleanupAndDelete(true, true);
- }
- catch (IException *E)
- {
- StringBuffer s;
- LOG(MCexception(E, MSGCLS_warning), E, s.append("Exception during deleteWorkUnit: ").append(wuid).str());
- E->Release();
- return false;
- }
- }
- removeWorkUnitFromAllQueues(wuid); //known active workunits wouldn't make it this far
- return true;
- }
- bool CWorkUnitFactory::deleteWorkUnit(const char * wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- return deleteWorkUnitEx(wuid, false, secmgr, secuser);
- }
- IConstWorkUnit* CWorkUnitFactory::openWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer wuidStr(wuid);
- wuidStr.trim();
- if (wuidStr.length() && ('w' == wuidStr.charAt(0)))
- wuidStr.setCharAt(0, 'W');
- if (!wuidStr.length() || ('W' != wuidStr.charAt(0)))
- {
- if (workUnitTraceLevel > 1)
- DBGLOG("openWorkUnit %s invalid WUID", nullText(wuidStr.str()));
- return NULL;
- }
- if (workUnitTraceLevel > 1)
- DBGLOG("openWorkUnit %s", wuidStr.str());
- Owned<IConstWorkUnit> wu = _openWorkUnit(wuid, secmgr, secuser);
- if (wu)
- {
- if (!checkWuSecAccess(*wu, secmgr, secuser, SecAccess_Read, "opening", true, true))
- return NULL; // Actually throws exception on failure, so won't reach here
- return wu.getClear();
- }
- else
- {
- if (workUnitTraceLevel > 0)
- IERRLOG("openWorkUnit %s not found", wuidStr.str());
- return NULL;
- }
- }
- IWorkUnit* CWorkUnitFactory::updateWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- if (workUnitTraceLevel > 1)
- DBGLOG("updateWorkUnit %s", wuid);
- Owned<CLocalWorkUnit> wu = _updateWorkUnit(wuid, secmgr, secuser);
- if (wu)
- {
- if (!checkWuSecAccess(*wu.get(), secmgr, secuser, SecAccess_Write, "updating", true, true))
- return NULL;
- return &wu->lockRemote(false);
- }
- else
- {
- if (workUnitTraceLevel > 0)
- IERRLOG("updateWorkUnit %s not found", wuid);
- return NULL;
- }
- }
- IPropertyTree * pruneBranch(IPropertyTree * from, char const * xpath)
- {
- Owned<IPropertyTree> ret;
- IPropertyTree * branch = from->queryPropTree(xpath);
- if(branch)
- {
- ret.setown(createPTreeFromIPT(branch));
- from->removeTree(branch);
- }
- return ret.getClear();
- }
- bool CWorkUnitFactory::restoreWorkUnit(const char *base, const char *wuid, bool restoreAssociated)
- {
- StringBuffer path(base);
- addPathSepChar(path).append(wuid).append(".xml");
- Owned<IPTree> pt = createPTreeFromXMLFile(path);
- if (!pt)
- return false;
- CDateTime dt;
- dt.setNow();
- StringBuffer dts;
- dt.getString(dts);
- pt->setProp("@restoredDate", dts.str());
- Owned<IPropertyTree> generatedDlls = pruneBranch(pt, "GeneratedDlls[1]");
- Owned<IPropertyTree> associatedFiles;
- IPropertyTree *srcAssociated = pt->queryPropTree("Query/Associated");
- if (srcAssociated)
- associatedFiles.setown(createPTreeFromIPT(srcAssociated));
- // The updating of the repo is implementation specific...
- if (!_restoreWorkUnit(pt.getClear(), wuid))
- return false;
- // now kludge back GeneratedDlls
- if (generatedDlls)
- {
- Owned<IPropertyTreeIterator> dlls = generatedDlls->getElements("GeneratedDll");
- for(dlls->first(); dlls->isValid(); dlls->next())
- {
- IPropertyTree & dll = dlls->query();
- char const * name = dll.queryProp("@name");
- char const * kind = dll.queryProp("@kind");
- char const * location = dll.queryProp("@location");
- Owned<IDllEntry> got = queryDllServer().getEntry(name);
- if (!got)
- {
- RemoteFilename dstRfn;
- dstRfn.setRemotePath(location);
- StringBuffer srcPath(base);
- addPathSepChar(srcPath);
- dstRfn.getTail(srcPath);
- OwnedIFile srcFile = createIFile(srcPath);
- OwnedIFile dstFile = createIFile(dstRfn);
- copyFile(dstFile, srcFile);
- queryDllServer().registerDll(name, kind, location);
- }
- }
- }
- if (associatedFiles)
- {
- Owned<IPropertyTreeIterator> associated = associatedFiles->getElements("*");
- ForEach(*associated)
- {
- IPropertyTree &file = associated->query();
- const char *filename = file.queryProp("@filename");
- SocketEndpoint ep(file.queryProp("@ip"));
- RemoteFilename rfn;
- rfn.setPath(ep, filename);
- OwnedIFile dstFile = createIFile(rfn);
- StringBuffer srcPath(base), name;
- addPathSepChar(srcPath);
- rfn.getTail(name);
- srcPath.append(name);
- if (generatedDlls)
- {
- VStringBuffer gDllPath("GeneratedDll[@name=\"%s\"]", name.str());
- if (generatedDlls->hasProp(gDllPath))
- continue; // generated dlls handled separately - see above
- }
- OwnedIFile srcFile = createIFile(srcPath);
- if (srcFile->exists())
- {
- try
- {
- copyFile(dstFile, srcFile);
- }
- catch (IException *e)
- {
- VStringBuffer msg("Failed to restore associated file '%s' to destination '%s'", srcFile->queryFilename(), dstFile->queryFilename());
- EXCLOG(e, msg.str());
- e->Release();
- }
- }
- }
- }
- return true;
- }
- void CWorkUnitFactory::importWorkUnit(const char *zapReportFileName, const char *zapReportPassword,
- const char *importDir, const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
- {
- class CImportWorkUnitHelper
- {
- StringAttr zapReportFileName, zapReportPassword, importDir, user, wuid, fromWUID, scope;
- StringBuffer unzipDir;
- Owned<IPTree> wuTree, graphProgressTree;
- bool findZAPFile(const char *mask, bool optional, StringBuffer &fileName)
- {
- Owned<IFile> d = createIFile(unzipDir);
- if (!d->exists())
- throw MakeStringException(WUERR_InvalidUserInput, "%s not found.", unzipDir.str());
- Owned<IDirectoryIterator> di = d->directoryFiles(mask, false, false);
- if (!di->first())
- {
- if (optional)
- return false;
- throw MakeStringException(WUERR_InvalidUserInput, "Failed to find %s in %s.", mask, unzipDir.str());
- }
- fileName.set(unzipDir).append(PATHSEPSTR);
- di->getName(fileName);
- if (di->next())
- throw MakeStringException(WUERR_InvalidUserInput, "More than 1 %s files found in %s.", mask, unzipDir.str());
- return true;
- }
- IPTree *readWUXMLFile(const char *pattern, bool optional)
- {
- StringBuffer fileName;
- if (!findZAPFile(pattern, optional, fileName))
- return nullptr;
- Owned<IPTree> tree = createPTreeFromXMLFile(fileName);
- if (!tree)
- throw MakeStringException(WUERR_InvalidUserInput, "Failed to retrieve %s.", pattern);
- Owned<IFile> f = createIFile(fileName);
- f->remove();
- return tree.getClear();
- }
- void updateWUQueryAssociatedFilesAttrs(const char *localIP)
- {
- ICopyArrayOf<IPropertyTree> fileTreesToRemove;
- IPropertyTree *queryAssociatedTreeRoot = wuTree->queryPropTree("Query/Associated");
- Owned<IPropertyTreeIterator> itr = queryAssociatedTreeRoot->getElements("File");
- ForEach (*itr)
- {
- IPropertyTree &fileTree = itr->query();
- const char *fileName = fileTree.queryProp("@filename");
- StringBuffer fileNameWithPath(unzipDir);
- fileNameWithPath.append(PATHSEPSTR).append(pathTail(fileName));
- if (checkFileExists(fileNameWithPath)) //Check if the ZAP report contains this file.
- {
- fileTree.setProp("@ip", localIP);
- fileTree.setProp("@filename", fileNameWithPath);
- }
- else
- fileTreesToRemove.append(fileTree);
- }
- ForEachItemIn(r, fileTreesToRemove)
- queryAssociatedTreeRoot->removeTree(&fileTreesToRemove.item(r));
- }
- void getWUAttributes(IWorkUnit *workunit)
- {
- wuid.set(workunit->queryWuid());
- scope.set(workunit->queryWuScope());
- }
- IPTree *queryWUPTree() const
- {
- return wuTree;
- }
- IPTree *queryGraphProgressPTree() const
- {
- return graphProgressTree;
- }
- void setUNZIPDir()
- { //Set a unique unzip folder inside the component's data folder
- unzipDir.append(importDir).append(PATHSEPSTR).append(wuid.get());
- }
- void unzipZAPReport()
- {
- Owned<IFile> unzipFolder = createIFile(unzipDir);
- if (!unzipFolder->exists())
- unzipFolder->createDirectory();
- else
- {//This should not happen. Just in case.
- throw MakeStringException(WUERR_CannotImportWorkunit, "Workunit %s had been created twice and passed to import()..", wuid.get());
- }
- //Unzip ZAP Report
- VStringBuffer zipCommand("unzip %s -d %s", zapReportFileName.get(), unzipDir.str());
- if (!isEmptyString(zapReportPassword))
- zipCommand.appendf(" -P %s", zapReportPassword.get());
- DWORD runcode;
- bool success = invoke_program(zipCommand.str(), runcode, true);
- if (!success)
- throw MakeStringException(WUERR_CannotImportWorkunit, "Failed in execution : %s.", zipCommand.str());
- if (runcode != 0)
- throw MakeStringException(WUERR_CannotImportWorkunit, "Failed in execution : %s, error(%" I64F "i)", zipCommand.str(), (unsigned __int64) runcode);
- }
- void buildPTreesFromZAPReport()
- {
- wuTree.setown(readWUXMLFile("ZAPReport_W*.xml", false));
- fromWUID.set(wuTree->queryName());
- wuTree->setProp("@scope", scope);
- //update QueryAssociatedFiles in WU XML;
- updateWUQueryAssociatedFilesAttrs(GetCachedHostName());
- graphProgressTree.setown(readWUXMLFile("ZAPReport_*.graphprogress", true));
- }
- void updateJobName(IWorkUnit *workunit)
- {
- StringBuffer newJobName;
- const char *jobName = workunit->queryJobName();
- if (isEmptyString(jobName))
- newJobName.appendf("IMPORTED from %s", fromWUID.get());
- else
- newJobName.appendf("IMPORTED: %s", jobName);
- workunit->setJobName(newJobName);
- }
- void setImportDebugAttribute(IWorkUnit *workunit)
- {
- StringBuffer attr, importDateTime;
- CDateTime dt;
- dt.setNow();
- dt.getString(importDateTime);
- attr.append("FromWUID=").append(fromWUID).append(",");
- attr.append("ImportDT=").append(importDateTime).append(",");
- attr.append("ZAPReport=").append(pathTail(zapReportFileName));
- workunit->setDebugValue("imported", attr, true);
- }
- public:
- CImportWorkUnitHelper(const char *_zapReportFileName, const char *_zapReportPassword, const char *_importDir, const char *_user)
- : zapReportFileName(_zapReportFileName), zapReportPassword(_zapReportPassword), importDir(_importDir), user(_user) { };
- void import(IWorkUnit *workunit)
- {
- getWUAttributes(workunit);
- setUNZIPDir();
- unzipZAPReport();
- buildPTreesFromZAPReport();
- workunit->import(queryWUPTree(), queryGraphProgressPTree());
- setImportDebugAttribute(workunit);
- updateJobName(workunit);
- removeFileTraceIfFail(zapReportFileName);
- }
- };
- Owned<IWorkUnit> newWU = createWorkUnit(app, user, secMgr, secUser);
- CImportWorkUnitHelper helper(zapReportFileName, zapReportPassword, importDir, user);
- helper.import(newWU);
- }
- int CWorkUnitFactory::setTracingLevel(int newLevel)
- {
- if (newLevel)
- DBGLOG("Setting workunit trace level to %d", newLevel);
- int level = workUnitTraceLevel;
- workUnitTraceLevel = newLevel;
- return level;
- }
- void CWorkUnitFactory::descheduleAllWorkUnits(ISecManager *secmgr, ISecUser *secuser)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/Schedule", myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if(!conn) return;
- Owned<IPropertyTree> root(conn->queryRoot()->getBranch("."));
- KeptAtomTable entries;
- Owned<IPropertyTreeIterator> iter(root->getElements("*/*/*/*"));
- StringBuffer wuid;
- for(iter->first(); iter->isValid(); iter->next())
- {
- char const * entry = iter->query().queryName();
- if(!entries.find(entry))
- {
- entries.addAtom(entry);
- ncnameUnescape(entry, wuid.clear());
- Owned<IWorkUnit> wu = updateWorkUnit(wuid, secmgr, secuser);
- if(wu && (wu->getState() == WUStateWait))
- wu->setState(WUStateCompleted);
- }
- }
- bool more;
- do more = root->removeProp("*"); while(more);
- }
- IConstQuerySetQueryIterator* CWorkUnitFactory::getQuerySetQueriesSorted( WUQuerySortField *sortorder, // list of fields to sort by (terminated by WUSFterm)
- WUQuerySortField *filters, // NULL or list of fields to filter on (terminated by WUSFterm)
- const void *filterbuf, // (appended) string values for filters
- unsigned startoffset,
- unsigned maxnum,
- __int64 *cachehint,
- unsigned *total,
- const MapStringTo<bool> *_subset,
- const MapStringTo<bool> *_suspendedQueriesByCluster)
- {
- struct PostFilters
- {
- WUQueryFilterBoolean activatedFilter;
- WUQueryFilterSuspended suspendedFilter;
- PostFilters()
- {
- activatedFilter = WUQFSAll;
- suspendedFilter = WUQFAllQueries;
- };
- } postFilters;
- class CQuerySetQueriesPager : public CSimpleInterface, implements IElementsPager
- {
- StringAttr querySet;
- StringAttr xPath;
- StringAttr sortOrder;
- PostFilters postFilters;
- StringArray unknownAttributes;
- const MapStringTo<bool> *subset;
- const MapStringTo<bool> *suspendedQueriesByCluster;
- void populateQueryTree(const IPropertyTree* querySetTree, IPropertyTree* queryTree)
- {
- const char* querySetId = querySetTree->queryProp("@id");
- std::unordered_set<std::string> aliasSet;
- Owned<IPropertyTreeIterator> aliasIter = querySetTree->getElements("Alias");
- ForEach(*aliasIter)
- {
- const char *id = aliasIter->query().queryProp("@id");
- if (!isEmptyString(id))
- aliasSet.insert(id);
- }
- VStringBuffer path("Query%s", xPath.get());
- Owned<IPropertyTreeIterator> iter = querySetTree->getElements(path.str());
- ForEach(*iter)
- {
- IPropertyTree &query = iter->query();
- bool activated = false;
- const char* queryId = query.queryProp("@id");
- if (queryId && *queryId)
- {
- if (subset)
- {
- VStringBuffer match("%s/%s", querySetId, queryId);
- if (!subset->getValue(match))
- continue;
- }
- if (aliasSet.find(queryId) != aliasSet.end())
- activated = true;
- }
- if (activated && (postFilters.activatedFilter == WUQFSNo))
- continue;
- if (!activated && (postFilters.activatedFilter == WUQFSYes))
- continue;
- bool isSuspendedByUser = query.hasProp(getEnumText(WUQSFSuspendedByUser,querySortFields));
- bool skip = false;
- switch(postFilters.suspendedFilter)
- {
- case WUQFSUSPDNo:
- if (isSuspendedByUser || checkSuspendedByCluster(querySetId, queryId))
- skip = true;
- break;
- case WUQFSUSPDYes:
- if (!isSuspendedByUser && !checkSuspendedByCluster(querySetId, queryId))
- skip = true;
- break;
- case WUQFSUSPDByUser:
- if (!isSuspendedByUser)
- skip = true;
- break;
- case WUQFSUSPDByFirstNode:
- case WUQFSUSPDByAnyNode:
- if (!checkSuspendedByCluster(querySetId, queryId))
- skip = true;
- break;
- }
- if (skip)
- continue;
- IPropertyTree *queryWithSetId = queryTree->addPropTree("Query", createPTreeFromIPT(&query));
- queryWithSetId->setProp("@querySetId", querySetId);
- queryWithSetId->setPropBool("@activated", activated);
- }
- }
- IRemoteConnection* populateQueryTree(IPropertyTree* queryTree)
- {
- StringBuffer querySetXPath("QuerySets");
- if (!querySet.isEmpty())
- querySetXPath.appendf("/QuerySet[@id=\"%s\"]", querySet.get());
- Owned<IRemoteConnection> conn = querySDS().connect(querySetXPath.str(), myProcessSession(), 0, SDS_LOCK_TIMEOUT);
- if (!conn)
- return NULL;
- IPropertyTree *root = conn->queryRoot()->queryBranch(nullptr);
- if (querySet.isEmpty())
- {
- Owned<IPropertyTreeIterator> querySetIter = root->getElements("*");
- ForEach(*querySetIter)
- populateQueryTree(&querySetIter->query(), queryTree);
- }
- else
- populateQueryTree(root, queryTree);
- return conn.getClear();
- }
- bool checkSuspendedByCluster(const char *querySetId, const char *queryId)
- {
- if (!suspendedQueriesByCluster)
- return false;
- VStringBuffer match("%s/%s", querySetId, queryId);
- bool *found = suspendedQueriesByCluster->getValue(match);
- return (found && *found);
- }
- public:
- IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
- CQuerySetQueriesPager(const char* _querySet, const char* _xPath, const char *_sortOrder, PostFilters& _postFilters,
- StringArray& _unknownAttributes, const MapStringTo<bool> *_subset, const MapStringTo<bool> *_suspendedQueriesByCluster)
- : querySet(_querySet), xPath(_xPath), sortOrder(_sortOrder), subset(_subset), suspendedQueriesByCluster(_suspendedQueriesByCluster)
- {
- postFilters.activatedFilter = _postFilters.activatedFilter;
- postFilters.suspendedFilter = _postFilters.suspendedFilter;
- ForEachItemIn(x, _unknownAttributes)
- unknownAttributes.append(_unknownAttributes.item(x));
- }
- virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree> &elements)
- {
- Owned<IPropertyTree> elementTree = createPTree("Queries");
- Owned<IRemoteConnection> conn = populateQueryTree(elementTree);
- if (!conn)
- return NULL;
- Owned<IPropertyTreeIterator> iter = elementTree->getElements("*");
- if (!iter)
- return NULL;
- sortElements(iter, sortOrder.get(), NULL, NULL, unknownAttributes, elements);
- return conn.getClear();
- }
- virtual bool allMatchingElementsReceived() { return true; } //For now, dali always returns all of matched Queries.
- };
- StringAttr querySet;
- StringBuffer xPath;
- StringBuffer so;
- StringArray unknownAttributes;
- if (filters)
- {
- const char *fv = (const char *)filterbuf;
- for (unsigned i=0;filters[i]!=WUQSFterm;i++) {
- int fmt = filters[i];
- int subfmt = (fmt&0xff);
- if (subfmt==WUQSFQuerySet)
- querySet.set(fv);
- else if ((subfmt==WUQSFmemoryLimit) || (subfmt==WUQSFtimeLimit) || (subfmt==WUQSFwarnTimeLimit) || (subfmt==WUQSFpriority))
- xPath.append('[').append(getEnumText(subfmt,querySortFields)).append(">=").append(fv).append("]");
- else if ((subfmt==WUQSFmemoryLimitHi) || (subfmt==WUQSFtimeLimitHi) || (subfmt==WUQSFwarnTimeLimitHi) || (subfmt==WUQSFpriorityHi))
- xPath.append('[').append(getEnumText(subfmt,querySortFields)).append("<=").append(fv).append("]");
- else if (subfmt==WUQSFActivited)
- postFilters.activatedFilter = (WUQueryFilterBoolean) atoi(fv);
- else if (subfmt==WUQSFSuspendedFilter)
- postFilters.suspendedFilter = (WUQueryFilterSuspended) atoi(fv);
- else if (!fv || !*fv)
- unknownAttributes.append(getEnumText(subfmt,querySortFields));
- else {
- xPath.append('[').append(getEnumText(subfmt,querySortFields)).append('=');
- if (fmt&WUQSFnocase)
- xPath.append('?');
- if (fmt&WUQSFnumeric)
- xPath.append('#');
- if (fmt&WUQSFwild)
- xPath.append('~');
- xPath.append('"').append(fv).append("\"]");
- }
- fv = fv + strlen(fv)+1;
- }
- }
- if (sortorder) {
- for (unsigned i=0;sortorder[i]!=WUQSFterm;i++) {
- if (so.length())
- so.append(',');
- int fmt = sortorder[i];
- if (fmt&WUQSFreverse)
- so.append('-');
- if (fmt&WUQSFnocase)
- so.append('?');
- if (fmt&WUQSFnumeric)
- so.append('#');
- so.append(getEnumText(fmt&0xff,querySortFields));
- }
- }
- IArrayOf<IPropertyTree> results;
- Owned<IElementsPager> elementsPager = new CQuerySetQueriesPager(querySet.get(), xPath.str(), so.length()?so.str():NULL,
- postFilters, unknownAttributes, _subset, _suspendedQueriesByCluster);
- Owned<IRemoteConnection> conn=getElementsPaged(elementsPager,startoffset,maxnum,NULL,"",cachehint,results,total,NULL);
- return new CConstQuerySetQueryIterator(results);
- }
- bool CWorkUnitFactory::isAborting(const char *wuid) const
- {
- VStringBuffer apath("/WorkUnitAborts/%s", wuid);
- try
- {
- Owned<IRemoteConnection> acon = querySDS().connect(apath.str(), myProcessSession(), 0, SDS_LOCK_TIMEOUT);
- if (acon)
- return acon->queryRoot()->getPropInt(NULL) != 0;
- }
- catch (IException *E)
- {
- EXCLOG(E);
- E->Release();
- }
- return false;
- }
- void CWorkUnitFactory::clearAborting(const char *wuid)
- {
- VStringBuffer apath("/WorkUnitAborts/%s", wuid);
- try
- {
- Owned<IRemoteConnection> acon = querySDS().connect(apath.str(), myProcessSession(), RTM_LOCK_WRITE|RTM_LOCK_SUB, SDS_LOCK_TIMEOUT);
- if (acon)
- acon->close(true);
- }
- catch (IException *E)
- {
- EXCLOG(E);
- E->Release();
- }
- }
- void CWorkUnitFactory::reportAbnormalTermination(const char *wuid, WUState &state, SessionId agent)
- {
- WARNLOG("reportAbnormalTermination: session stopped unexpectedly: %" I64F "d state: %d", (__int64) agent, (int) state);
- bool isEcl = false;
- switch (state)
- {
- case WUStateAborting:
- state = WUStateAborted;
- break;
- case WUStateCompiling:
- isEcl = true;
- // drop into
- default:
- state = WUStateFailed;
- }
- Owned<IWorkUnit> wu = updateWorkUnit(wuid, NULL, NULL);
- wu->setState(state);
- Owned<IWUException> e = wu->createException();
- e->setExceptionCode(isEcl ? 1001 : 1000);
- e->setExceptionMessage(isEcl ? "EclCC terminated unexpectedly" : "Workunit terminated unexpectedly");
- }
- static CriticalSection deleteDllLock;
- static IWorkQueueThread *deleteDllWorkQ = nullptr;
- MODULE_INIT(INIT_PRIORITY_STANDARD)
- {
- return true;
- }
- MODULE_EXIT()
- {
- CriticalBlock b(deleteDllLock);
- if (deleteDllWorkQ)
- ::Release(deleteDllWorkQ);
- deleteDllWorkQ = nullptr;
- }
- static void asyncRemoveDll(const char * name)
- {
- CriticalBlock b(deleteDllLock);
- if (!deleteDllWorkQ)
- deleteDllWorkQ = createWorkQueueThread();
- deleteDllWorkQ->post(new asyncRemoveDllWorkItem(name));
- }
- static void asyncRemoveFile(const char * ip, const char * name)
- {
- CriticalBlock b(deleteDllLock);
- if (!deleteDllWorkQ)
- deleteDllWorkQ = createWorkQueueThread();
- deleteDllWorkQ->post(new asyncRemoveRemoteFileWorkItem(ip, name));
- }
- class CDaliWorkUnitFactory : public CWorkUnitFactory, implements IDaliClientShutdown
- {
- public:
- IMPLEMENT_IINTERFACE_USING(CWorkUnitFactory);
- CDaliWorkUnitFactory()
- {
- // Assumes dali client configuration has already been done
- sdsManager = &querySDS();
- session = myProcessSession();
- addShutdownHook(*this);
- }
- ~CDaliWorkUnitFactory()
- {
- removeShutdownHook(*this);
- }
- virtual bool initializeStore()
- {
- throwUnexpected(); // Used when loading a plugin factory - not applicable here
- }
- virtual IWorkUnitWatcher *getWatcher(IWorkUnitSubscriber *subscriber, WUSubscribeOptions options, const char *wuid) const
- {
- return new CDaliWorkUnitWatcher(subscriber, options, wuid);
- }
- virtual unsigned validateRepository(bool fixErrors)
- {
- return 0;
- }
- virtual void deleteRepository(bool recreate)
- {
- Owned<IRemoteConnection> conn = sdsManager->connect("/WorkUnits", session, RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if (conn)
- conn->close(true);
- conn.setown(sdsManager->connect("/GraphProgress", session, RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
- if (conn)
- conn->close(true);
- }
- virtual void createRepository()
- {
- // Nothing to do
- }
- virtual const char *queryStoreType() const
- {
- return "Dali";
- }
- virtual CLocalWorkUnit *_createWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- IRemoteConnection *conn;
- conn = sdsManager->connect(wuRoot.str(), session, RTM_LOCK_WRITE|RTM_CREATE_UNIQUE, SDS_LOCK_TIMEOUT);
- conn->queryRoot()->setProp("@xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance");
- conn->queryRoot()->setPropInt("@wuidVersion", WUID_VERSION);
- conn->queryRoot()->setProp("@totalThorTime", "");
- return new CDaliWorkUnit(conn, secmgr, secuser);
- }
- virtual CLocalWorkUnit* _openWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- IRemoteConnection* conn = sdsManager->connect(wuRoot.str(), session, 0, SDS_LOCK_TIMEOUT);
- if (conn)
- return new CDaliWorkUnit(conn, secmgr, secuser);
- else
- return NULL;
- }
- virtual bool _restoreWorkUnit(IPTree *_pt, const char *wuid)
- {
- Owned<IPTree> pt(_pt);
- Owned<IPropertyTree> gprogress = pruneBranch(pt, "GraphProgress[1]");
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- Owned<IRemoteConnection> conn = sdsManager->connect(wuRoot.str(), myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- if (!conn)
- {
- ERRLOG("restoreWorkUnit could not create to %s", wuRoot.str());
- return false;
- }
- IPropertyTree *root = conn->queryRoot();
- if (root->hasChildren())
- {
- ERRLOG("restoreWorkUnit WUID %s already exists", wuid);
- return false;
- }
- root->setPropTree(NULL, pt.getClear());
- conn.clear();
- // now kludge back GraphProgress
- if (gprogress)
- {
- VStringBuffer xpath("/GraphProgress/%s", wuid);
- conn.setown(querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT));
- if (conn)
- {
- IPropertyTree *groot = conn->queryRoot();
- if (groot->hasChildren())
- WARNLOG("restoreWorkUnit WUID %s graphprogress already exists, replacing",wuid);
- groot->setPropTree(NULL, gprogress.getClear());
- }
- }
- return true;
- }
- virtual CLocalWorkUnit* _updateWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- IRemoteConnection* conn = sdsManager->connect(wuRoot.str(), session, RTM_LOCK_WRITE|RTM_LOCK_SUB, SDS_LOCK_TIMEOUT);
- if (conn)
- return new CDaliWorkUnit(conn, secmgr, secuser);
- else
- return NULL;
- }
- virtual IWorkUnit* getGlobalWorkUnit(ISecManager *secmgr, ISecUser *secuser)
- {
- // MORE - should it check security?
- StringBuffer wuRoot;
- getXPath(wuRoot, GLOBAL_WORKUNIT);
- IRemoteConnection* conn = sdsManager->connect(wuRoot.str(), session, RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- conn->queryRoot()->setProp("@xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance");
- Owned<CLocalWorkUnit> cw = new CDaliWorkUnit(conn, (ISecManager *) NULL, NULL);
- return &cw->lockRemote(false);
- }
- virtual IConstWorkUnitIterator* getWorkUnitsByOwner(const char * owner, ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer path("*");
- if (owner && *owner)
- path.append("[@submitID=?~\"").append(owner).append("\"]");
- return _getWorkUnitsByXPath(path.str(), secmgr, secuser);
- }
- IConstWorkUnitIterator* getScheduledWorkUnits(ISecManager *secmgr, ISecUser *secuser)
- {
- StringBuffer path("*");
- path.append("[@state=\"").append(getEnumText(WUStateScheduled, states)).append("\"]");
- return _getWorkUnitsByXPath(path.str(), secmgr, secuser);
- }
- virtual void clientShutdown();
- virtual unsigned numWorkUnits()
- {
- Owned<IRemoteConnection> conn = sdsManager->connect("/WorkUnits", session, 0, SDS_LOCK_TIMEOUT);
- if (!conn)
- return 0;
- IPropertyTree *root = conn->queryRoot();
- return root->numChildren();
- }
- /**
- * Add a filter to an xpath query, with the appropriate filter flags
- */
- static void appendFilterToQueryString(StringBuffer& query, int flags, const char* name, const char* value)
- {
- query.append('[').append(name).append('=');
- if (flags & WUSFnocase)
- query.append('?');
- if (flags & WUSFwild)
- query.append('~');
- query.append('"').append(value).append("\"]");
- };
- IConstWorkUnitIterator* getWorkUnitsSorted( WUSortField sortorder, // field to sort by (and flags for desc sort etc)
- WUSortField *filters, // NULL or list of fields to filter on (terminated by WUSFterm)
- const void *filterbuf, // (appended) string values for filters
- unsigned startoffset,
- unsigned maxnum,
- __int64 *cachehint,
- unsigned *total,
- ISecManager *secmgr,
- ISecUser *secuser)
- {
- class CQueryOrFilter : public CInterface
- {
- unsigned flags;
- StringAttr name;
- StringArray values;
- public:
- IMPLEMENT_IINTERFACE;
- CQueryOrFilter(unsigned _flags, const char *_name, const char *_value)
- : flags(_flags), name(_name)
- {
- values.appendListUniq(_value, "|");
- };
- const char* queryName() { return name.get(); };
- unsigned querySearchFlags() { return flags; };
- const StringArray& queryValues() const { return values; };
- };
- class CMultiPTreeIterator : public CInterfaceOf<IPropertyTreeIterator>
- {
- public:
- virtual bool first() override
- {
- curSource = 0;
- while (sources.isItem(curSource))
- {
- if (sources.item(curSource).first())
- return true;
- curSource++;
- }
- return false;
- }
- virtual bool next() override
- {
- if (sources.isItem(curSource))
- {
- if (sources.item(curSource).next())
- return true;
- curSource++;
- while (sources.isItem(curSource))
- {
- if (sources.item(curSource).first())
- return true;
- curSource++;
- }
- }
- return false;
- }
- virtual bool isValid() override
- {
- return sources.isItem(curSource);
- }
- virtual IPropertyTree & query() override
- {
- return sources.item(curSource).query();
- }
- void addSource(IPropertyTreeIterator &source)
- {
- sources.append(source);
- }
- private:
- IArrayOf<IPropertyTreeIterator> sources;
- unsigned curSource = 0;
- };
- class CWorkUnitsPager : public CSimpleInterface, implements IElementsPager
- {
- StringAttr xPath;
- StringAttr sortOrder;
- StringAttr nameFilterLo;
- StringAttr nameFilterHi;
- StringArray unknownAttributes;
- Owned<CQueryOrFilter> orFilter;
- public:
- IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
- CWorkUnitsPager(const char* _xPath, CQueryOrFilter* _orFilter, const char *_sortOrder, const char* _nameFilterLo, const char* _nameFilterHi, StringArray& _unknownAttributes)
- : xPath(_xPath), orFilter(_orFilter), sortOrder(_sortOrder), nameFilterLo(_nameFilterLo), nameFilterHi(_nameFilterHi)
- {
- ForEachItemIn(x, _unknownAttributes)
- unknownAttributes.append(_unknownAttributes.item(x));
- }
- virtual IRemoteConnection* getElements(IArrayOf<IPropertyTree> &elements)
- {
- Owned<IRemoteConnection> conn = querySDS().connect("WorkUnits", myProcessSession(), 0, SDS_LOCK_TIMEOUT);
- if (!conn)
- return NULL;
- Owned<IPropertyTreeIterator> iter;
- if (!orFilter)
- {
- iter.setown(conn->getElements(xPath.get()));
- }
- else
- {
- Owned <CMultiPTreeIterator> multi = new CMultiPTreeIterator;
- bool added = false;
- const char* fieldName = orFilter->queryName();
- unsigned flags = orFilter->querySearchFlags();
- const StringArray& values = orFilter->queryValues();
- ForEachItemIn(i, values)
- {
- StringBuffer path(xPath.get());
- const char* value = values.item(i);
- if (!isEmptyString(value))
- {
- appendFilterToQueryString(path, flags, fieldName, value);
- IPropertyTreeIterator *itr = conn->getElements(path.str());
- if (itr)
- {
- multi->addSource(*itr);
- added = true;
- }
- }
- }
- if (added)
- iter.setown(multi.getClear());
- }
- if (!iter)
- return NULL;
- sortElements(iter, sortOrder.get(), nameFilterLo.get(), nameFilterHi.get(), unknownAttributes, elements);
- return conn.getClear();
- }
- virtual bool allMatchingElementsReceived() { return true; }//For now, dali always returns all of matched WUs.
- };
- class CScopeChecker : public CSimpleInterface, implements ISortedElementsTreeFilter
- {
- UniqueScopes done;
- ISecManager *secmgr;
- ISecUser *secuser;
- CriticalSection crit;
- public:
- IMPLEMENT_IINTERFACE_USING(CSimpleInterface);
- CScopeChecker(ISecManager *_secmgr,ISecUser *_secuser)
- {
- secmgr = _secmgr;
- secuser = _secuser;
- }
- bool isOK(IPropertyTree &tree)
- {
- const char *scopename = tree.queryProp("@scope");
- if (!scopename||!*scopename)
- return true;
- {
- CriticalBlock block(crit);
- const bool *b = done.getValue(scopename);
- if (b)
- return *b;
- }
- bool ret = checkWuScopeSecAccess(scopename,secmgr,secuser,SecAccess_Read,"iterating",false,false);
- {
- // conceivably could have already been checked and added, but ok.
- CriticalBlock block(crit);
- done.setValue(scopename,ret);
- }
- return ret;
- }
- };
- Owned<CQueryOrFilter> orFilter;
- Owned<ISortedElementsTreeFilter> sc = new CScopeChecker(secmgr,secuser);
- StringBuffer query;
- StringBuffer so;
- StringAttr namefilter("*");
- StringAttr namefilterlo;
- StringAttr namefilterhi;
- StringArray unknownAttributes;
- if (filters)
- {
- const char *fv = (const char *) filterbuf;
- for (unsigned i=0;filters[i]!=WUSFterm;i++)
- {
- assertex(fv);
- int fmt = filters[i];
- int subfmt = (fmt&0xff);
- if (subfmt==WUSFwuid)
- namefilterlo.set(fv);
- else if (subfmt==WUSFwuidhigh)
- namefilterhi.set(fv);
- else if (subfmt==WUSFwildwuid)
- namefilter.set(fv);
- else if (subfmt==WUSFappvalue)
- {
- const char *app = fv;
- fv = fv + strlen(fv)+1;
- query.append("[Application/").append(app);
- if (*fv)
- query.append("=?~\"").append(fv).append('\"');
- query.append("]");
- }
- else if (subfmt==WUSFtotalthortime)
- {
- query.append("[@totalThorTime>=\"");
- formatTimeCollatable(query, milliToNano(atoi(fv)), false);
- query.append("\"]");
- }
- else if (!*fv)
- {
- unknownAttributes.append(getEnumText(subfmt,workunitSortFields));
- if (subfmt==WUSFtotalthortime)
- sortorder = (WUSortField) (sortorder & ~WUSFnumeric);
- }
- else
- {
- const char* fieldName = getEnumText(subfmt,workunitSortFields);
- if (!strchr(fv, '|'))
- appendFilterToQueryString(query, fmt, fieldName, fv);
- else if (orFilter)
- throw MakeStringException(WUERR_InvalidUserInput, "Multiple OR filters not allowed");
- else
- {
- if (!strieq(fieldName, getEnumText(WUSFstate,workunitSortFields)) &&
- !strieq(fieldName, getEnumText(WUSFuser,workunitSortFields)) &&
- !strieq(fieldName, getEnumText(WUSFcluster,workunitSortFields)))
- throw MakeStringException(WUERR_InvalidUserInput, "OR filters not allowed for %s", fieldName);
- orFilter.setown(new CQueryOrFilter(fmt, fieldName, fv));
- }
- }
- fv = fv + strlen(fv)+1;
- }
- }
- if ((sortorder&0xff)==WUSFtotalthortime)
- sortorder = (WUSortField) (sortorder & ~WUSFnumeric);
- query.insert(0, namefilter.get());
- if (sortorder)
- {
- if (so.length())
- so.append(',');
- if (sortorder & WUSFreverse)
- so.append('-');
- if (sortorder & WUSFnocase)
- so.append('?');
- if (sortorder & WUSFnumeric)
- so.append('#');
- so.append(getEnumText(sortorder&0xff,workunitSortFields));
- }
- IArrayOf<IPropertyTree> results;
- Owned<IElementsPager> elementsPager = new CWorkUnitsPager(query.str(), orFilter.getClear(), so.length()?so.str():NULL, namefilterlo.get(), namefilterhi.get(), unknownAttributes);
- Owned<IRemoteConnection> conn=getElementsPaged(elementsPager,startoffset,maxnum,secmgr?sc:NULL,"",cachehint,results,total,NULL);
- return new CConstWUArrayIterator(results);
- }
- virtual WUState waitForWorkUnit(const char * wuid, unsigned timeout, bool compiled, std::list<WUState> expectedStates)
- {
- WUState ret = WUStateUnknown;
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- Owned<IRemoteConnection> conn = sdsManager->connect(wuRoot.str(), session, 0, SDS_LOCK_TIMEOUT);
- if (timeout == 0) //no need to subscribe
- {
- ret = (WUState) getEnum(conn->queryRoot(), "@state", states);
- auto it = std::find(expectedStates.begin(), expectedStates.end(), ret);
- if (it != expectedStates.end())
- return ret;
- switch (ret)
- {
- case WUStateCompiled:
- case WUStateUploadingFiles:
- if (!compiled)
- break;
- //fall through
- case WUStateCompleted:
- case WUStateFailed:
- case WUStateAborted:
- return ret;
- default:
- break;
- }
- return WUStateUnknown;
- }
- Owned<WorkUnitWaiter> waiter = new WorkUnitWaiter(wuid, SubscribeOptionState);
- LocalIAbortHandler abortHandler(*waiter);
- if (conn)
- {
- SessionId agent = -1;
- bool agentSessionStopped = false;
- unsigned start = msTick();
- for (;;)
- {
- ret = (WUState) getEnum(conn->queryRoot(), "@state", states);
- auto it = std::find(expectedStates.begin(), expectedStates.end(), ret);
- if (it != expectedStates.end())
- return ret;
- switch (ret)
- {
- case WUStateCompiled:
- case WUStateUploadingFiles:
- if (!compiled)
- break;
- // fall into
- case WUStateCompleted:
- case WUStateFailed:
- case WUStateAborted:
- return ret;
- case WUStateWait:
- break;
- case WUStateCompiling:
- case WUStateRunning:
- case WUStateDebugPaused:
- case WUStateDebugRunning:
- case WUStateBlocked:
- case WUStateAborting:
- if (agentSessionStopped)
- {
- reportAbnormalTermination(wuid, ret, agent);
- return ret;
- }
- if (queryDaliServerVersion().compare("2.1")>=0)
- {
- agent = conn->queryRoot()->getPropInt64("@agentSession", -1);
- if((agent>0) && querySessionManager().sessionStopped(agent, 0))
- {
- agentSessionStopped = true;
- conn->reload();
- continue;
- }
- }
- break;
- }
- agentSessionStopped = false; // reset for state changes such as WUStateWait then WUStateRunning again
- unsigned waited = msTick() - start;
- if (timeout==-1 || waited + 20000 < timeout)
- {
- waiter->wait(20000); // recheck state every 20 seconds, in case eclagent has crashed.
- if (waiter->isAborted())
- {
- ret = WUStateUnknown; // MORE - throw an exception?
- break;
- }
- }
- else if (waited > timeout || !waiter->wait(timeout-waited))
- {
- ret = WUStateUnknown; // MORE - throw an exception?
- break;
- }
- conn->reload();
- }
- }
- return ret;
- }
- virtual WUAction waitForWorkUnitAction(const char * wuid, WUAction original)
- {
- Owned<WorkUnitWaiter> waiter = new WorkUnitWaiter(wuid, SubscribeOptionAction);
- LocalIAbortHandler abortHandler(*waiter);
- WUAction ret = WUActionUnknown;
- StringBuffer wuRoot;
- getXPath(wuRoot, wuid);
- Owned<IRemoteConnection> conn = sdsManager->connect(wuRoot.str(), session, 0, SDS_LOCK_TIMEOUT);
- if (conn)
- {
- unsigned start = msTick();
- for (;;)
- {
- ret = (WUAction) getEnum(conn->queryRoot(), "Action", actions);
- if (ret != original)
- break;
- unsigned waited = msTick() - start;
- waiter->wait(20000); // recheck state every 20 seconds even if no timeout, in case eclagent has crashed.
- if (waiter->isAborted())
- {
- ret = WUActionUnknown; // MORE - throw an exception?
- break;
- }
- conn->reload();
- }
- }
- waiter->unsubscribe();
- return ret;
- }
- protected:
- IConstWorkUnitIterator * _getWorkUnitsByXPath(const char *xpath, ISecManager *secmgr, ISecUser *secuser)
- {
- Owned<IRemoteConnection> conn = sdsManager->connect("/WorkUnits", session, 0, SDS_LOCK_TIMEOUT);
- if (conn)
- {
- CDaliVersion serverVersionNeeded("3.2");
- Owned<IPropertyTreeIterator> iter(queryDaliServerVersion().compare(serverVersionNeeded) < 0 ?
- conn->queryRoot()->getElements(xpath) :
- conn->getElements(xpath));
- return createSecureConstWUIterator(iter.getClear(), secmgr, secuser);
- }
- else
- return NULL;
- }
- ISDSManager *sdsManager;
- SessionId session;
- };
- extern WORKUNIT_API IConstWorkUnitIterator *createSecureConstWUIterator(IConstWorkUnitIterator *iter, ISecManager *secmgr, ISecUser *secuser)
- {
- if (secmgr)
- return new CSecureConstWUIterator(iter, secmgr, secuser);
- else
- return iter;
- }
- extern WORKUNIT_API IConstWorkUnitIterator *createSecureConstWUIterator(IPropertyTreeIterator *iter, ISecManager *secmgr, ISecUser *secuser)
- {
- if (secmgr)
- return new CSecureConstWUIterator(new CConstWUIterator(iter), secmgr, secuser);
- else
- return new CConstWUIterator(iter);
- }
- static CriticalSection factoryCrit;
- static Owned<IWorkUnitFactory> globalFactory;
- void CDaliWorkUnitFactory::clientShutdown()
- {
- CriticalBlock b(factoryCrit);
- globalFactory.clear();
- }
- void clientShutdownWorkUnit()
- {
- CriticalBlock b(factoryCrit);
- globalFactory.clear();
- }
- extern WORKUNIT_API void setWorkUnitFactory(IWorkUnitFactory * _factory)
- {
- CriticalBlock b(factoryCrit);
- globalFactory.setown(_factory);
- }
- extern WORKUNIT_API IWorkUnitFactory * getWorkUnitFactory()
- {
- if (!globalFactory)
- {
- CriticalBlock b(factoryCrit);
- 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.
- {
- const char *forceEnv = getenv("FORCE_DALI_WORKUNITS");
- bool forceDali = forceEnv && !strieq(forceEnv, "off") && !strieq(forceEnv, "0");
- Owned<IRemoteConnection> env = querySDS().connect("/Environment", myProcessSession(), 0, SDS_LOCK_TIMEOUT);
- IPropertyTree *pluginInfo = NULL;
- if (env)
- {
- SocketEndpoint targetDali = queryCoven().queryGroup().queryNode(0).endpoint();
- IPropertyTree *daliInfo = findDaliProcess(env->queryRoot(), targetDali);
- if (daliInfo)
- {
- const char *daliName = daliInfo->queryProp("@name");
- if (daliName)
- {
- VStringBuffer xpath("Software/DaliServerPlugin[@type='WorkunitServer'][@daliServers='%s']", daliName);
- pluginInfo = env->queryRoot()->queryPropTree(xpath);
- }
- if (!pluginInfo)
- pluginInfo = daliInfo->queryPropTree("Plugin[@type='WorkunitServer']"); // Compatibility with early betas of 6.0 ...
- }
- }
- if (pluginInfo && !forceDali)
- globalFactory.setown( (IWorkUnitFactory *) loadPlugin(pluginInfo));
- else
- globalFactory.setown(new CDaliWorkUnitFactory());
- }
- }
- return globalFactory.getLink();
- }
- extern WORKUNIT_API IWorkUnitFactory * getDaliWorkUnitFactory()
- {
- if (!globalFactory)
- {
- CriticalBlock b(factoryCrit);
- 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.
- globalFactory.setown(new CDaliWorkUnitFactory());
- }
- return globalFactory.getLink();
- }
- // A SecureWorkUnitFactory allows the security params to be supplied once to the factory rather than being supplied to each call.
- // They can still be supplied if you want...
- class CSecureWorkUnitFactory : implements IWorkUnitFactory, public CInterface
- {
- public:
- IMPLEMENT_IINTERFACE;
- CSecureWorkUnitFactory(IWorkUnitFactory *_baseFactory, ISecManager *_secMgr, ISecUser *_secUser)
- : baseFactory(_baseFactory), defaultSecMgr(_secMgr), defaultSecUser(_secUser)
- {
- }
- virtual bool initializeStore()
- {
- throwUnexpected(); // Used when loading a plugin factory - not applicable here
- }
- virtual IWorkUnitWatcher *getWatcher(IWorkUnitSubscriber *subscriber, WUSubscribeOptions options, const char *wuid) const
- {
- return baseFactory->getWatcher(subscriber, options, wuid);
- }
- virtual unsigned validateRepository(bool fix)
- {
- return baseFactory->validateRepository(fix);
- }
- virtual void deleteRepository(bool recreate)
- {
- return baseFactory->deleteRepository(recreate);
- }
- virtual void createRepository()
- {
- return baseFactory->createRepository();
- }
- virtual const char *queryStoreType() const
- {
- return baseFactory->queryStoreType();
- }
- virtual StringArray &getUniqueValues(WUSortField field, const char *prefix, StringArray &result) const
- {
- return baseFactory->getUniqueValues(field, prefix, result);
- }
- virtual IWorkUnit* createNamedWorkUnit(const char *wuid, const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->createNamedWorkUnit(wuid, app, user, secMgr, secUser);
- }
- virtual IWorkUnit* createWorkUnit(const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->createWorkUnit(app, user, secMgr, secUser);
- }
- virtual bool deleteWorkUnit(const char * wuid, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->deleteWorkUnit(wuid, secMgr, secUser);
- }
- virtual bool deleteWorkUnitEx(const char * wuid, bool throwException, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->deleteWorkUnitEx(wuid, throwException, secMgr, secUser);
- }
- virtual IConstWorkUnit* openWorkUnit(const char *wuid, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->openWorkUnit(wuid, secMgr, secUser);
- }
- virtual IWorkUnit* updateWorkUnit(const char *wuid, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->updateWorkUnit(wuid, secMgr, secUser);
- }
- virtual bool restoreWorkUnit(const char *base, const char *wuid, bool restoreAssociated)
- {
- return baseFactory->restoreWorkUnit(base, wuid, restoreAssociated);
- }
- virtual void importWorkUnit(const char *zapReportFileName, const char *zapReportPassword,
- const char *importDir, const char *app, const char *user, ISecManager *secMgr, ISecUser *secUser)
- {
- baseFactory->importWorkUnit(zapReportFileName, zapReportPassword, importDir, app, user, secMgr, secUser);
- }
- virtual IWorkUnit * getGlobalWorkUnit(ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->getGlobalWorkUnit(secMgr, secUser);
- }
- virtual IConstWorkUnitIterator * getWorkUnitsByOwner(const char * owner, ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->getWorkUnitsByOwner(owner, secMgr, secUser);
- }
- virtual IConstWorkUnitIterator * getScheduledWorkUnits(ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->getScheduledWorkUnits(secMgr, secUser);
- }
- virtual void descheduleAllWorkUnits(ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- baseFactory->descheduleAllWorkUnits(secMgr, secUser);
- }
- virtual int setTracingLevel(int newLevel)
- {
- return baseFactory->setTracingLevel(newLevel);
- }
- virtual IConstWorkUnitIterator* getWorkUnitsSorted( WUSortField sortorder, // field to sort by
- WUSortField *filters, // NULL or list of fields to filter on (terminated by WUSFterm)
- const void *filterbuf, // (appended) string values for filters
- unsigned startoffset,
- unsigned maxnum,
- __int64 *cachehint,
- unsigned *total,
- ISecManager *secMgr, ISecUser *secUser)
- {
- if (!secMgr) secMgr = defaultSecMgr.get();
- if (!secUser) secUser = defaultSecUser.get();
- return baseFactory->getWorkUnitsSorted(sortorder,filters,filterbuf,startoffset,maxnum,cachehint, total, secMgr, secUser);
- }
- virtual IConstQuerySetQueryIterator* getQuerySetQueriesSorted( WUQuerySortField *sortorder,
- WUQuerySortField *filters,
- const void *filterbuf,
- unsigned startoffset,
- unsigned maxnum,
- __int64 *cachehint,
- unsigned *total,
- const MapStringTo<bool> *subset,
- const MapStringTo<bool> *suspendedByCluster)
- {
- // MORE - why no security?
- return baseFactory->getQuerySetQueriesSorted(sortorder,filters,filterbuf,startoffset,maxnum,cachehint,total,subset,suspendedByCluster);
- }
- virtual unsigned numWorkUnits()
- {
- return baseFactory->numWorkUnits();
- }
- virtual bool isAborting(const char *wuid) const
- {
- return baseFactory->isAborting(wuid);
- }
- virtual void clearAborting(const char *wuid)
- {
- baseFactory->clearAborting(wuid);
- }
- virtual WUState waitForWorkUnit(const char * wuid, unsigned timeout, bool compiled, std::list<WUState> expectedStates)
- {
- return baseFactory->waitForWorkUnit(wuid, timeout, compiled, expectedStates);
- }
- virtual WUAction waitForWorkUnitAction(const char * wuid, WUAction original)
- {
- return baseFactory->waitForWorkUnitAction(wuid, original);
- }
- private:
- Owned<IWorkUnitFactory> baseFactory;
- Linked<ISecManager> defaultSecMgr;
- Linked<ISecUser> defaultSecUser;
- };
- extern WORKUNIT_API IWorkUnitFactory * getWorkUnitFactory(ISecManager *secmgr, ISecUser *secuser)
- {
- if (secmgr && secuser)
- return new CSecureWorkUnitFactory(getWorkUnitFactory(), secmgr, secuser);
- else
- return getWorkUnitFactory();
- }
- //==========================================================================================
- class CStringPTreeIterator : implements IStringIterator, public CInterface
- {
- Owned<IPropertyTreeIterator> it;
- public:
- IMPLEMENT_IINTERFACE;
- CStringPTreeIterator(IPropertyTreeIterator *p) : it(p) {};
- virtual bool first() { return it->first(); }
- virtual bool next() { return it->next(); }
- virtual bool isValid() { return it->isValid(); }
- virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryProp(NULL)); return s; }
- };
- class CStringPTreeTagIterator : implements IStringIterator, public CInterface
- {
- Owned<IPropertyTreeIterator> it;
- public:
- IMPLEMENT_IINTERFACE;
- CStringPTreeTagIterator(IPropertyTreeIterator *p) : it(p) {};
- virtual bool first() { return it->first(); }
- virtual bool next() { return it->next(); }
- virtual bool isValid() { return it->isValid(); }
- virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryName()); return s; }
- };
- class CStringPTreeAttrIterator : implements IStringIterator, public CInterface
- {
- Owned<IPropertyTreeIterator> it;
- StringAttr name;
- public:
- IMPLEMENT_IINTERFACE;
- CStringPTreeAttrIterator(IPropertyTreeIterator *p, const char *_name) : it(p), name(_name) {};
- virtual bool first() { return it->first(); }
- virtual bool next() { return it->next(); }
- virtual bool isValid() { return it->isValid(); }
- virtual IStringVal & str(IStringVal &s) { s.set(it->query().queryProp(name)); return s; }
- };
- //==========================================================================================
- CLocalWorkUnit::CLocalWorkUnit(ISecManager *secmgr, ISecUser *secuser)
- {
- clearCached(false);
- secMgr.set(secmgr);
- secUser.set(secuser);
- workflowIteratorCached = false;
- resultsCached = false;
- graphsCached = false;
- temporariesCached = false;
- variablesCached = false;
- exceptionsCached = false;
- pluginsCached = false;
- librariesCached = false;
- activitiesCached = false;
- webServicesInfoCached = false;
- roxieQueryInfoCached = false;
- }
- void CLocalWorkUnit::clearCached(bool clearTree)
- {
- query.clear();
- webServicesInfo.clear();
- workflowIterator.clear();
- graphs.kill();
- results.kill();
- variables.kill();
- plugins.kill();
- libraries.kill();
- exceptions.kill();
- temporaries.kill();
- statistics.kill();
- appvalues.kill();
- if (clearTree)
- p.clear();
- workflowIteratorCached = false;
- resultsCached = false;
- graphsCached = false;
- temporariesCached = false;
- variablesCached = false;
- exceptionsCached = false;
- pluginsCached = false;
- librariesCached = false;
- activitiesCached = false;
- webServicesInfoCached = false;
- roxieQueryInfoCached = false;
- }
- void CLocalWorkUnit::loadPTree(IPropertyTree *ptree)
- {
- clearCached(false);
- p.setown(ptree);
- }
- void CLocalWorkUnit::beforeDispose()
- {
- try
- {
- unsubscribe();
- clearCached(true);
- userDesc.clear();
- secMgr.clear();
- secUser.clear();
- }
- catch (IException *E) { LOG(MCexception(E, MSGCLS_warning), E, "Exception during ~CLocalWorkUnit"); E->Release(); }
- }
- void CLocalWorkUnit::cleanupAndDelete(bool deldll, bool deleteOwned, const StringArray *deleteExclusions)
- {
- MTIME_SECTION(queryActiveTimer(), "WUDELETE cleanupAndDelete total");
- // Delete any related things in SDS etc that might otherwise be forgotten
- if (p->getPropBool("@protected", false))
- throw MakeStringException(WUERR_WorkunitProtected, "%s: Workunit is protected",p->queryName());
- switch (getState())
- {
- case WUStateAborted:
- case WUStateCompleted:
- case WUStateFailed:
- case WUStateArchived:
- break;
- case WUStateCompiled:
- if (getAction()==WUActionRun || getAction()==WUActionUnknown)
- throw MakeStringException(WUERR_WorkunitActive, "%s: Workunit is active. Please abort before deleting this workunit.",p->queryName());
- break;
- case WUStateWait:
- throw MakeStringException(WUERR_WorkunitScheduled, "%s: Workunit is scheduled",p->queryName());
- default:
- throw MakeStringException(WUERR_WorkunitActive, "%s: Workunit is active. Please abort before deleting this workunit.",p->queryName());
- break;
- }
- if (getIsQueryService())
- {
- Owned<IPropertyTree> registry = getQueryRegistryRoot();
- if (registry)
- {
- VStringBuffer xpath("QuerySet/Query[@wuid='%s']", p->queryName());
- if (registry->hasProp(xpath.str()))
- throw MakeStringException(WUERR_WorkunitPublished, "%s: Workunit is published",p->queryName());
- }
- }
- try
- {
- if (deldll && !p->getPropBool("@isClone", false))
- {
- Owned<IConstWUQuery> q = getQuery();
- if (q)
- {
- Owned<IConstWUAssociatedFileIterator> iter = &q->getAssociatedFiles();
- SCMStringBuffer name;
- ForEach(*iter)
- {
- IConstWUAssociatedFile & cur = iter->query();
- cur.getNameTail(name);
- if (!deleteExclusions || (NotFound == deleteExclusions->find(name.str())))
- {
- Owned<IDllEntry> entry = queryDllServer().getEntry(name.str());
- if (entry.get())
- asyncRemoveDll(name.str());
- else
- {
- SCMStringBuffer ip, localPath;
- cur.getName(localPath);
- cur.getIp(ip);
- asyncRemoveFile(ip.str(), localPath.str());
- }
- }
- }
- }
- }
- globalFactory->clearAborting(queryWuid());
- deleteTempFiles(NULL, deleteOwned, true); // all, any remaining.
- }
- catch(IException *E)
- {
- StringBuffer s;
- LOG(MCexception(E, MSGCLS_warning), E, s.append("Exception during cleanupAndDelete: ").append(p->queryName()).str());
- E->Release();
- }
- catch (...)
- {
- WARNLOG("Unknown exception during cleanupAndDelete: %s", p->queryName());
- }
- }
- void CLocalWorkUnit::setTimeScheduled(const IJlibDateTime &val)
- {
- SCMStringBuffer strval;
- val.getGmtString(strval);
- p->setProp("@timescheduled",strval.str());
- }
- IJlibDateTime & CLocalWorkUnit::getTimeScheduled(IJlibDateTime &val) const
- {
- StringBuffer str;
- p->getProp("@timescheduled",str);
- if(str.length())
- val.setGmtString(str.str());
- return val;
- }
- bool modifyAndWriteWorkUnitXML(char const * wuid, StringBuffer & buf, StringBuffer & extra, IFileIO * fileio)
- {
- // kludge in extra chunks of XML such as GraphProgress and GeneratedDlls
- if (extra.length())
- {
- const char *bufStart = buf.str();
- const char *bufPtr = bufStart+buf.length();
- while (bufStart != bufPtr)
- {
- --bufPtr;
- if (!isspace(*bufPtr))
- break;
- }
- assertex('>' == *bufPtr);
- size_t l = strlen(wuid);
- assertex((size_t)(bufPtr-bufStart) > l+2); // e.g. at least </W20171111-111111>
- bufPtr -= l+2; // skip back over </wuid
- assertex(0 == memcmp(bufPtr, "</", 2) );
- assertex(0 == memcmp(bufPtr+2, wuid, l));
- buf.insert(bufPtr-bufStart, extra);
- }
- return (fileio->write(0,buf.length(),buf.str()) == buf.length());
- }
- bool CLocalWorkUnit::archiveWorkUnit(const char *base, bool del, bool ignoredllerrors, bool deleteOwned, bool exportAssociatedFiles)
- {
- CriticalBlock block(crit);
- StringBuffer path(base);
- if (!p)
- return false;
- const char *wuid = p->queryName();
- if (!wuid||!*wuid)
- return false;
- addPathSepChar(path).append(wuid).append(".xml");
- Owned<IFile> file = createIFile(path.str());
- if (!file)
- return false;
- Owned<IFileIO> fileio = file->open(IFOcreate);
- if (!fileio)
- return false;
- StringBuffer buf;
- exportWorkUnitToXML(this, buf, false, false, true);
- StringBuffer extraWorkUnitXML;
- Owned<IPTree> graphProgress = getGraphProgressTree();
- if (graphProgress)
- {
- toXML(graphProgress,extraWorkUnitXML,1,XML_Format);
- graphProgress.clear();
- }
- Owned<IConstWUQuery> q = getQuery();
- if (!q)
- {
- if (!modifyAndWriteWorkUnitXML(wuid, buf, extraWorkUnitXML, fileio))
- return false;
- if (del)
- {
- if (getState()==WUStateUnknown)
- setState(WUStateArchived); // to allow delete
- cleanupAndDelete(false,deleteOwned); // no query, may as well delete
- }
- return false;
- }
- StringArray deleteExclusions; // associated files not to delete, added if failure to copy
- Owned<IConstWUAssociatedFileIterator> iter = &q->getAssociatedFiles();
- Owned<IPropertyTree> generatedDlls = createPTree("GeneratedDlls");
- ForEach(*iter)
- {
- IConstWUAssociatedFile & cur = iter->query();
- SCMStringBuffer name;
- cur.getNameTail(name);
- if (name.length())
- {
- Owned<IDllEntry> entry = queryDllServer().getEntry(name.str());
- SCMStringBuffer curPath, curIp;
- cur.getName(curPath);
- cur.getIp(curIp);
- SocketEndpoint curEp(curIp.str());
- RemoteFilename curRfn;
- curRfn.setPath(curEp, curPath.str());
- StringBuffer dst(base);
- addPathSepChar(dst);
- curRfn.getTail(dst);
- Owned<IFile> dstFile = createIFile(dst.str());
- if (entry.get())
- {
- Owned<IException> exception;
- Owned<IDllLocation> loc;
- Owned<IPropertyTree> generatedDllBranch = createPTree();
- generatedDllBranch->setProp("@name", entry->queryName());
- generatedDllBranch->setProp("@kind", entry->queryKind());
- if (exportAssociatedFiles)
- {
- try
- {
- loc.setown(entry->getBestLocation()); //throws exception if no readable locations
- }
- catch(IException * e)
- {
- exception.setown(e);
- loc.setown(entry->getBestLocationCandidate()); //this will be closest of the unreadable locations
- }
- RemoteFilename filename;
- loc->getDllFilename(filename);
- if (!exception)
- {
- Owned<IFile> srcfile = createIFile(filename);
- try
- {
- if (dstFile->exists())
- {
- if (streq(srcfile->queryFilename(), dstFile->queryFilename()))
- deleteExclusions.append(name.str()); // restored workunit, referencing archive location for query dll (no longer true post HPCC-11191 fix)
- // still want to delete if already archived but there are source file copies
- }
- else
- copyFile(dstFile, srcfile);
- }
- catch(IException * e)
- {
- exception.setown(e);
- }
- }
- if (exception)
- {
- if (ignoredllerrors)
- {
- EXCLOG(exception.get(), "archiveWorkUnit (copying associated file)");
- //copy failed, so don't delete the registered dll files
- deleteExclusions.append(name.str());
- }
- else
- throw exception.getClear();
- }
- }
- // Record Associated path to restore back to
- StringBuffer restorePath;
- curRfn.getRemotePath(restorePath);
- generatedDllBranch->setProp("@location", restorePath.str());
- generatedDlls->addPropTree("GeneratedDll", generatedDllBranch.getClear());
- }
- else if (exportAssociatedFiles) // no generated dll entry
- {
- Owned<IFile> srcFile = createIFile(curRfn);
- try
- {
- copyFile(dstFile, srcFile);
- }
- catch (IException *e)
- {
- VStringBuffer msg("Failed to archive associated file '%s' to destination '%s'", srcFile->queryFilename(), dstFile->queryFilename());
- EXCLOG(e, msg.str());
- e->Release();
- deleteExclusions.append(name.str());
- }
- }
- }
- }
- iter.clear();
- if (generatedDlls->numChildren())
- toXML(generatedDlls, extraWorkUnitXML, 1, XML_Format);
- if (!modifyAndWriteWorkUnitXML(wuid, buf, extraWorkUnitXML, fileio))
- return false;
- if (del)
- {
- //setState(WUStateArchived); // this isn't useful as about to delete it!
- q.clear();
- cleanupAndDelete(true, deleteOwned, &deleteExclusions);
- }
- return true;
- }
- void CLocalWorkUnit::loadXML(const char *xml)
- {
- CriticalBlock block(crit);
- clearCached(true);
- assertex(xml);
- p.setown(createPTreeFromXMLString(xml,ipt_lowmem));
- }
- void CLocalWorkUnit::serialize(MemoryBuffer &tgt)
- {
- CriticalBlock block(crit);
- StringBuffer x;
- tgt.append(exportWorkUnitToXML(this, x, false, false, false).str());
- }
- void CLocalWorkUnit::deserialize(MemoryBuffer &src)
- {
- CriticalBlock block(crit);
- StringAttr value;
- src.read(value);
- loadXML(value);
- }
- void CLocalWorkUnit::requestAbort()
- {
- CriticalBlock block(crit);
- abortWorkUnit(p->queryName());
- }
- void CLocalWorkUnit::unlockRemote()
- {
- CriticalBlock block(crit);
- locked.unlock();
- _unlockRemote();
- }
- IWorkUnit &CLocalWorkUnit::lockRemote(bool commit)
- {
- if (secMgr)
- checkWuSecAccess(*this, secMgr.get(), secUser.get(), SecAccess_Write, "write lock", true, true);
- locked.lock();
- CriticalBlock block(crit);
- if (commit)
- {
- try
- {
- _lockRemote();
- }
- catch (IException *E)
- {
- StringBuffer s;
- IERRLOG("Failed to get write lock on workunit: %s", E->errorMessage(s).str());
- locked.unlock();
- throw;
- }
- }
- return *new CLockedWorkUnit(LINK(this));
- }
- void CLocalWorkUnit::commit()
- {
- // Nothing to do if not backed by a persistent store
- }
- IWorkUnit& CLocalWorkUnit::lock()
- {
- return lockRemote(true);
- }
- IConstWorkUnit *CLocalWorkUnit::unlock()
- {
- unlockRemote();
- return this;
- }
- const char *CLocalWorkUnit::queryWuid() const
- {
- CriticalBlock block(crit);
- return p->queryName();
- }
- unsigned CLocalWorkUnit::getDebugAgentListenerPort() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("@DebugListenerPort", 0);
- }
- unsigned CLocalWorkUnit::getTotalThorTime() const
- {
- CriticalBlock block(crit);
- if (p->hasProp("@totalThorTime"))
- return (unsigned)nanoToMilli(extractTimeCollatable(p->queryProp("@totalThorTime"), nullptr));
- const WuScopeFilter filter("stype[graph],nested[0],stat[TimeElapsed]");
- StatsAggregation summary;
- aggregateStatistic(summary, (IConstWorkUnit *)this, filter, StTimeElapsed);
- return (unsigned)nanoToMilli(summary.getSum());
- }
- void CLocalWorkUnit::setDebugAgentListenerPort(unsigned port)
- {
- CriticalBlock block(crit);
- p->setPropInt("@DebugListenerPort", port);
- }
- IStringVal& CLocalWorkUnit::getDebugAgentListenerIP(IStringVal &ip) const
- {
- CriticalBlock block(crit);
- ip.set(p->queryProp("@DebugListenerIP"));
- return ip;
- }
- void CLocalWorkUnit::setDebugAgentListenerIP(const char * ip)
- {
- CriticalBlock block(crit);
- p->setProp("@DebugListenerIP", ip);
- }
- IStringVal & CLocalWorkUnit::getWorkunitDistributedAccessToken(IStringVal & datoken) const
- {
- CriticalBlock block(crit);
- datoken.set(p->queryProp("@distributedAccessToken"));
- return datoken;
- }
- bool CLocalWorkUnit::setDistributedAccessToken(const char * user)
- {
- CriticalBlock block(crit);
- //If this format changes, you must update the isWorkunitDAToken() and extractFromWorkunitDAToken() methods
- VStringBuffer datoken("HPCC[u=%s,w=%s]", user, queryWuid());
- IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
- if (pDSM && pDSM->isDigiSignerConfigured())
- {
- datoken.append(pDSM->queryKeyName()).append(';');
- StringBuffer b64Signature;
- if (pDSM->digiSign(b64Signature, datoken))
- {
- datoken.append(b64Signature.str());
- }
- else
- {
- ERRLOG("Cannot create workunit Distributed Access Token, digisign failed");
- return false;
- }
- }
- else
- {
- if (workUnitTraceLevel > 1)
- WARNLOG("Cannot sign Distributed Access Token, digisign signing not configured");
- datoken.append(";");
- }
- p->setProp("@distributedAccessToken", datoken);
- return true;
- }
- bool CLocalWorkUnit::getRunningGraph(IStringVal &graphName, WUGraphIDType &subId) const
- {
- // Only implemented in derived classes
- return false;
- }
- void CLocalWorkUnit::setJobName(const char *value)
- {
- CriticalBlock block(crit);
- p->setProp("@jobName", value);
- }
- const char *CLocalWorkUnit::queryJobName() const
- {
- CriticalBlock block(crit);
- const char *ret = p->queryProp("@jobName");
- if (!ret)
- ret = "";
- return ret;
- }
- void CLocalWorkUnit::setClusterName(const char *value)
- {
- CriticalBlock block(crit);
- p->setProp("@clusterName", value);
- }
- const char *CLocalWorkUnit::queryClusterName() const
- {
- CriticalBlock block(crit);
- const char *ret = p->queryProp("@clusterName");
- if (!ret)
- ret = "";
- return ret;
- }
- void CLocalWorkUnit::setAllowedClusters(const char *value)
- {
- setDebugValue("allowedclusters",value, true);
- }
- IStringVal& CLocalWorkUnit::getAllowedClusters(IStringVal &str) const
- {
- CriticalBlock block(crit);
- getDebugValue("allowedclusters",str);
- if (str.length()!=0)
- return str;
- str.set(p->queryProp("@clusterName"));
- return str;
- }
- void CLocalWorkUnit::setAllowAutoQueueSwitch(bool val)
- {
- setDebugValueInt("allowautoqueueswitch",val?1:0,true);
- }
- bool CLocalWorkUnit::getAllowAutoQueueSwitch() const
- {
- CriticalBlock block(crit);
- return getDebugValueBool("allowautoqueueswitch",false);
- }
- void CLocalWorkUnit::setLibraryInformation(const char * name, unsigned interfaceHash, unsigned definitionHash)
- {
- StringBuffer suffix;
- if (name && *name)
- setApplicationValue("LibraryModule", "name", name, true);
- setApplicationValueInt("LibraryModule", "interfaceHash", interfaceHash, true);
- setApplicationValueInt("LibraryModule", "definitionHash", definitionHash, true);
- setApplicationValue("LibraryModule", "platform", appendLibrarySuffix(suffix).str(), true);
- }
- void CLocalWorkUnit::remoteCheckAccess(IUserDescriptor *user, bool writeaccess) const
- {
- unsigned auditflags = DALI_LDAP_AUDIT_REPORT|DALI_LDAP_READ_WANTED;
- if (writeaccess)
- auditflags |= DALI_LDAP_WRITE_WANTED;
- SecAccessFlags perm = SecAccess_Full;
- const char *scopename = p->queryProp("@scope");
- if (scopename&&*scopename) {
- if (!user)
- user = queryUserDescriptor();
- perm = querySessionManager().getPermissionsLDAP("workunit",scopename,user,auditflags);
- if (perm<0) {
- if (perm == SecAccess_Unavailable)
- perm = SecAccess_Full;
- else
- perm = SecAccess_None;
- }
- }
- if (!HASREADPERMISSION(perm))
- throw MakeStringException(WUERR_WorkunitAccessDenied, "Read access denied for workunit %s", queryWuid());
- if (writeaccess && !HASWRITEPERMISSION(perm))
- throw MakeStringException(WUERR_WorkunitAccessDenied, "Write access denied for workunit %s", queryWuid());
- }
- void CLocalWorkUnit::setUser(const char * value)
- {
- CriticalBlock block(crit);
- p->setProp("@submitID", value);
- }
- const char *CLocalWorkUnit::queryUser() const
- {
- CriticalBlock block(crit);
- const char *ret = p->queryProp("@submitID");
- if (!ret)
- ret = "";
- return ret;
- }
- void CLocalWorkUnit::setWuScope(const char * value)
- {
- if (value && *value)
- {
- if (checkWuScopeSecAccess(value, secMgr.get(), secUser.get(), SecAccess_Write, "Change Scope", true, true))
- {
- CriticalBlock block(crit);
- p->setProp("@scope", value);
- }
- }
- }
- const char *CLocalWorkUnit::queryWuScope() const
- {
- CriticalBlock block(crit);
- const char *ret = p->queryProp("@scope");
- if (!ret)
- ret = "";
- return ret;
- }
- void CLocalWorkUnit::setPriority(WUPriorityClass cls)
- {
- CriticalBlock block(crit);
- setEnum(p, "@priorityClass", cls, priorityClasses);
- }
- WUPriorityClass CLocalWorkUnit::getPriority() const
- {
- CriticalBlock block(crit);
- return (WUPriorityClass) getEnum(p, "@priorityClass", priorityClasses);
- }
- const char *CLocalWorkUnit::queryPriorityDesc() const
- {
- return getEnumText(getPriority(), priorityClasses);
- }
- void CLocalWorkUnit::setState(WUState value)
- {
- if (value==WUStateAborted || value==WUStatePaused || value==WUStateCompleted || value==WUStateFailed || value==WUStateSubmitted || value==WUStateWait)
- {
- if (globalFactory)
- globalFactory->clearAborting(queryWuid());
- }
- CriticalBlock block(crit);
- setEnum(p, "@state", value, states); // For historical reasons, we use state to store the state
- setEnum(p, "State", value, states); // But we can only subscribe to elements, not attributes
- if (getDebugValueBool("monitorWorkunit", false))
- {
- switch(value)
- {
- case WUStateAborted:
- FLLOG(MCoperatorWarning, "Workunit %s aborted", p->queryName());
- break;
- case WUStateCompleted:
- FLLOG(MCoperatorProgress, "Workunit %s completed", p->queryName());
- break;
- case WUStateFailed:
- FLLOG(MCoperatorProgress, "Workunit %s failed", p->queryName());
- break;
- }
- }
- p->removeProp("@stateEx");
- }
- void CLocalWorkUnit::setStateEx(const char * text)
- {
- CriticalBlock block(crit);
- p->setProp("@stateEx", text);
- }
- void CLocalWorkUnit::setAgentSession(__int64 sessionId)
- {
- CriticalBlock block(crit);
- p->setPropInt64("@agentSession", sessionId);
- }
- bool CLocalWorkUnit::getIsQueryService() const
- {
- CriticalBlock block(crit);
- return p->getPropBool("@isQueryService", false);
- }
- void CLocalWorkUnit::setIsQueryService(bool value)
- {
- CriticalBlock block(crit);
- p->setPropBool("@isQueryService", value);
- }
- void CLocalWorkUnit::checkAgentRunning(WUState & state)
- {
- if (queryDaliServerVersion().compare("2.1")<0)
- return;
- switch(state)
- {
- case WUStateRunning:
- case WUStateDebugPaused:
- case WUStateDebugRunning:
- case WUStateBlocked:
- case WUStateAborting:
- case WUStateCompiling:
- case WUStatePaused:
- {
- SessionId agent = getAgentSession();
- if((agent>0) && querySessionManager().sessionStopped(agent, 0))
- {
- forceReload();
- state = (WUState) getEnum(p, "@state", states);
- bool isecl=state==WUStateCompiling;
- if (aborting())
- state = WUStateAborted;
- else if (state==WUStateRunning || state==WUStatePaused || state==WUStateDebugPaused || state==WUStateDebugRunning || state==WUStateBlocked || state==WUStateCompiling)
- state = WUStateFailed;
- else
- return;
- WARNLOG("checkAgentRunning terminated: %" I64F "d state = %d",(__int64)agent,(int)state);
- Owned<IWorkUnit> w = &lock();
- w->setState(state);
- Owned<IWUException> e = w->createException();
- WUAction action = w->getAction();
- switch (action)
- {
- case WUActionPause:
- case WUActionPauseNow:
- case WUActionResume:
- w->setAction(WUActionUnknown);
- }
- if(isecl)
- {
- e->setExceptionCode(1001);
- e->setExceptionMessage("EclServer terminated unexpectedly");
- }
- else
- {
- e->setExceptionCode(1000);
- e->setExceptionMessage("Workunit terminated unexpectedly");
- }
- }
- }
- }
- }
- WUState CLocalWorkUnit::getState() const
- {
- CriticalBlock block(crit);
- WUState state = (WUState) getEnum(p, "@state", states);
- switch (state)
- {
- case WUStateRunning:
- case WUStateDebugPaused:
- case WUStateDebugRunning:
- case WUStateBlocked:
- case WUStateCompiling:
- if (aborting())
- state = WUStateAborting;
- break;
- case WUStateSubmitted:
- if (aborting())
- state = WUStateAborted;
- break;
- }
- const_cast<CLocalWorkUnit *>(this)->checkAgentRunning(state); //need const_cast as will change state if agent has died
- return state;
- }
- IStringVal& CLocalWorkUnit::getStateEx(IStringVal & str) const
- {
- CriticalBlock block(crit);
- str.set(p->queryProp("@stateEx"));
- return str;
- }
- __int64 CLocalWorkUnit::getAgentSession() const
- {
- CriticalBlock block(crit);
- return p->getPropInt64("@agentSession", -1);
- }
- unsigned CLocalWorkUnit::getAgentPID() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("@agentPID", -1);
- }
- const char * CLocalWorkUnit::queryStateDesc() const
- {
- // MORE - not sure about this - may prefer a separate interface
- CriticalBlock block(crit);
- try
- {
- return getEnumText(getState(), states);
- }
- catch (...)
- {
- return "???";
- }
- }
- void CLocalWorkUnit::setAction(WUAction value)
- {
- CriticalBlock block(crit);
- setEnum(p, "Action", value, actions);
- }
- WUAction CLocalWorkUnit::getAction() const
- {
- CriticalBlock block(crit);
- return (WUAction) getEnum(p, "Action", actions);
- }
- const char *CLocalWorkUnit::queryActionDesc() const
- {
- CriticalBlock block(crit);
- return p->queryProp("Action");
- }
- bool CLocalWorkUnit::hasApplicationValue(const char *app, const char *propname) const
- {
- StringBuffer prop("Application/");
- prop.append(app).append('/').append(propname);
- CriticalBlock block(crit);
- return p->hasProp(prop);
- }
- IStringVal& CLocalWorkUnit::getApplicationValue(const char *app, const char *propname, IStringVal &str) const
- {
- StringBuffer prop("Application/");
- prop.append(app).append('/').append(propname);
- CriticalBlock block(crit);
- str.set(p->queryProp(prop.str()));
- return str;
- }
- int CLocalWorkUnit::getApplicationValueInt(const char *app, const char *propname, int defVal) const
- {
- StringBuffer prop("Application/");
- prop.append(app).append('/').append(propname);
- CriticalBlock block(crit);
- return p->getPropInt(prop.str(), defVal);
- }
- IConstWUAppValueIterator& CLocalWorkUnit::getApplicationValues() const
- {
- CriticalBlock block(crit);
- appvalues.load(p,"Application/*");
- return *new CArrayIteratorOf<IConstWUAppValue,IConstWUAppValueIterator> (appvalues, 0, (IConstWorkUnit *) this);
- }
- void CLocalWorkUnit::setApplicationValue(const char *app, const char *propname, const char *value, bool overwrite)
- {
- CriticalBlock block(crit);
- StringBuffer prop("Application/");
- prop.append(app).append('/').append(propname);
- if (overwrite || !p->hasProp(prop.str()))
- {
- StringBuffer sp;
- p->setProp(sp.append("Application").str(), "");
- p->setProp(sp.append('/').append(app).str(), "");
- p->setProp(prop.str(), value);
- }
- }
- void CLocalWorkUnit::setApplicationValueInt(const char *app, const char *propname, int value, bool overwrite)
- {
- VStringBuffer s("%d", value);
- setApplicationValue(app, propname, s, overwrite);
- }
- void CLocalWorkUnit::setPriorityLevel(int level)
- {
- CriticalBlock block(crit);
- p->setPropInt("PriorityFlag", level);
- }
- int CLocalWorkUnit::getPriorityLevel() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("PriorityFlag");
- }
- int calcPriorityValue(const IPropertyTree * p)
- {
- int priority = p->getPropInt("PriorityFlag");
- switch((WUPriorityClass) getEnum(p, "@priorityClass", priorityClasses))
- {
- case PriorityClassLow:
- priority -= 100;
- break;
- case PriorityClassHigh:
- priority += 100;
- break;
- }
- return priority;
- }
- int CLocalWorkUnit::getPriorityValue() const
- {
- CriticalBlock block(crit);
- return calcPriorityValue(p);
- }
- void CLocalWorkUnit::setRescheduleFlag(bool value)
- {
- CriticalBlock block(crit);
- p->setPropInt("RescheduleFlag", (int) value);
- }
- bool CLocalWorkUnit::getRescheduleFlag() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("RescheduleFlag") != 0;
- }
- class NullIStringIterator : implements IStringIterator, public CInterface
- {
- public:
- IMPLEMENT_IINTERFACE;
- bool first() { return false; }
- bool next() { return false; }
- bool isValid() { return false; }
- IStringVal & str(IStringVal & str) { return str; }
- };
- ClusterType getClusterType(const char * platform, ClusterType dft)
- {
- if (stricmp(platform, "thor") == 0)
- return ThorLCRCluster;
- if (stricmp(platform, "thorlcr") == 0)
- return ThorLCRCluster;
- if (stricmp(platform, "hthor") == 0)
- return HThorCluster;
- if (stricmp(platform, "roxie") == 0)
- return RoxieCluster;
- return dft;
- }
- const char *clusterTypeString(ClusterType clusterType, bool lcrSensitive)
- {
- switch (clusterType)
- {
- case ThorLCRCluster:
- if (lcrSensitive)
- return "thorlcr";
- return "thor";
- case RoxieCluster:
- return "roxie";
- case HThorCluster:
- return "hthor";
- }
- throwUnexpected();
- }
- bool extractFromWorkunitDAToken(const char * distributedAccessToken, StringBuffer * wuid, StringBuffer * user, StringBuffer * privKey)
- {
- if (!isWorkunitDAToken(distributedAccessToken))
- {
- DBGLOG("Not a valid workunit distributed access token");
- return false;
- }
- //Extract the string between [ and ]
- const char * pEndBracket = strchr(distributedAccessToken + 5, ']');
- StringBuffer tokenValues;
- tokenValues.append(pEndBracket - distributedAccessToken - 5, distributedAccessToken + 5);
- StringArray nameValues;
- nameValues.appendList(tokenValues.str(), ",",true);
- if (user || wuid)
- {
- for (int x = 0; x < nameValues.ordinality(); x++)
- {
- if (user && 0==strncmp(nameValues[x],"u=",2))//Extract user
- user->append(nameValues[x] + 2);
- if (wuid && 0==strncmp(nameValues[x],"w=",2))//Extract wuid
- wuid->append(nameValues[x] + 2);
- }
- }
- if (privKey)
- {
- const char * finger = pEndBracket;
- ++finger;
- while (*finger && *finger != ';')
- privKey->append(1, finger++);
- }
- return true;
- }
- //Verifies the given signed workunit distributed access token was created
- //and signed by the given wuid and user, and that the workunit is still active
- //Returns:
- // 0 : Success, token is valid and workunit is active
- // 1 : Signature does not verify (wuid/username don't match, or signature does not verify)
- // 2 : Workunit not active
- // Throws if unable to open workunit
- wuTokenStates verifyWorkunitDAToken(const char * ctxUser, const char * daToken)
- {
- #ifdef _CONTAINERIZED
- if (!getComponentConfigSP()->getPropBool("@wuTokens", false))
- return wuTokenInvalid;
- #endif
- if (isEmptyString(daToken))
- {
- ERRLOG("verifyWorkunitDAToken : Token must be provided");
- return wuTokenInvalid;
- }
- StringBuffer tokWuid;
- StringBuffer tokUser;
- if (!extractFromWorkunitDAToken(daToken, &tokWuid, &tokUser, nullptr))//get the wuid and user
- {
- //Not a valid workunit distributed access token
- return wuTokenInvalid;
- }
- //Validate signature
- IDigitalSignatureManager * pDSM = queryDigitalSignatureManagerInstanceFromEnv();
- if (pDSM && pDSM->isDigiVerifierConfigured())
- {
- const char * finger;
- StringBuffer token;//receives copy of everything up until signature
- for (finger = daToken; *finger && *finger != ';'; finger++)
- token.append(1, finger);
- token.append(1, finger);//append ;
- StringBuffer sig(++finger);
- if (!pDSM->digiVerify(sig, token))
- {
- ERRLOG("verifyWorkunitDAToken : workunit distributed access token does not verify");
- return wuTokenInvalid;
- }
- }
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- Owned<IConstWorkUnit> cw = factory->openWorkUnit(tokWuid.str());
- if(!cw)
- {
- throw MakeStringException(WUERR_WorkunitAccessDenied,"verifyWorkunitDAToken : Cannot open workunit %s",tokWuid.str());
- }
- //Verify user matches
- bool wuUserExist = !isEmptyString(cw->queryUser());
- bool tokUserExist = !isEmptyString(tokUser.str());
- bool ctxUserExist = !isEmptyString(ctxUser);
- if (wuUserExist && tokUserExist)
- {
- //if both users are found, they must match
- if (!streq(tokUser.str(), cw->queryUser()))
- {
- ERRLOG("verifyWorkunitDAToken : Token user (%s) does not match WU user (%s)", tokUser.str(), cw->queryUser());
- return wuTokenInvalid;//Possible Internal error
- }
- else if (ctxUserExist && !streq(tokUser.str(), ctxUser))//ctxUser will be empty if security not enabled
- {
- ERRLOG("verifyWorkunitDAToken : Token user (%s) does not match Context user (%s)", cw->queryUser(), ctxUser);
- return wuTokenInvalid;
- }
- }
- else if (!wuUserExist && !tokUserExist)//both users will be empty if no security enabled
- {
- if (ctxUserExist)
- {
- ERRLOG("verifyWorkunitDAToken : Security enabled but WU user and Token user not specified");
- return wuTokenInvalid;
- }
- //both users empty and no context user means if no security enabled
- }
- else
- {
- //one user found, but not the other, treat as an error
- ERRLOG("verifyWorkunitDAToken : WU user %s and Token user %s must be provided", wuUserExist ? cw->queryUser() : "(NULL)", tokUserExist ? tokUser.str() : "(NULL)");
- return wuTokenInvalid;
- }
- // no need to compare tokWuid with workunit wuid, because it will always match
- bool wuActive;
- switch (cw->getState())
- {
- case WUStateRunning:
- case WUStateDebugRunning:
- case WUStateBlocked:
- case WUStateAborting:
- case WUStateUploadingFiles:
- #ifdef _DEBUG
- DBGLOG("verifyWorkunitDAToken : Workunit token validated for %s %s, state is '%s'", cw->queryWuid(), cw->queryUser(), getWorkunitStateStr(cw->getState()));
- #endif
- wuActive = true;
- break;
- default:
- ERRLOG("verifyWorkunitDAToken : Workunit %s not active, state is '%s'", cw->queryWuid(), getWorkunitStateStr(cw->getState()));
- wuActive = false;
- break;
- }
- return wuActive ? wuTokenValid : wuTokenWorkunitInactive;
- }
- bool CLocalWorkUnit::resolveFilePrefix(StringBuffer & prefix, const char * queue) const
- {
- if (hasApplicationValue("prefix", queue))
- {
- getApplicationValue("prefix", queue, StringBufferAdaptor(prefix));
- return true;
- }
- #ifndef _CONTAINERIZED
- Owned<IConstWUClusterInfo> ci = getTargetClusterInfo(queue);
- if (ci)
- {
- ci->getScope(StringBufferAdaptor(prefix));
- return true;
- }
- #endif
- return false;
- }
- IStringVal& CLocalWorkUnit::getScope(IStringVal &str) const
- {
- StringBuffer prefix;
- CriticalBlock block(crit);
- if (p->hasProp("Debug/ForceScope"))
- {
- prefix.append(p->queryProp("Debug/ForceScope")).toLowerCase();
- }
- else
- {
- resolveFilePrefix(prefix, p->queryProp("@clusterName"));
- }
- str.set(prefix.str());
- return str;
- }
- //Queries
- void CLocalWorkUnit::setCodeVersion(unsigned codeVersion, const char * buildVersion, const char * eclVersion)
- {
- CriticalBlock block(crit);
- p->setPropInt("@codeVersion", codeVersion);
- p->setProp("@buildVersion", buildVersion);
- p->setProp("@eclVersion", eclVersion);
- }
- unsigned CLocalWorkUnit::getCodeVersion() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("@codeVersion");
- }
- unsigned CLocalWorkUnit::getWuidVersion() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("@wuidVersion");
- }
- void CLocalWorkUnit::getBuildVersion(IStringVal & buildVersion, IStringVal & eclVersion) const
- {
- CriticalBlock block(crit);
- buildVersion.set(p->queryProp("@buildVersion"));
- eclVersion.set(p->queryProp("@eclVersion"));
- }
- void CLocalWorkUnit::setCloneable(bool value)
- {
- CriticalBlock block(crit);
- p->setPropInt("@cloneable", value);
- }
- void CLocalWorkUnit::setIsClone(bool value)
- {
- CriticalBlock block(crit);
- p->setPropInt("@isClone", value);
- }
- bool CLocalWorkUnit::getCloneable() const
- {
- CriticalBlock block(crit);
- return p->getPropBool("@cloneable", false);
- }
- IUserDescriptor *CLocalWorkUnit::queryUserDescriptor() const
- {
- CriticalBlock block(crit);
- if (!userDesc)
- {
- userDesc.setown(createUserDescriptor());
- SCMStringBuffer token;
- getWorkunitDistributedAccessToken(token);
- userDesc->set(queryUser(), token.str());//use token as password
- }
- return userDesc;
- }
- bool CLocalWorkUnit::isProtected() const
- {
- CriticalBlock block(crit);
- return p->getPropBool("@protected", false);
- }
- bool CLocalWorkUnit::isPausing() const
- {
- CriticalBlock block(crit);
- if (WUActionPause == getAction())
- {
- switch (getState())
- {
- case WUStateRunning:
- case WUStateAborting:
- return true;
- }
- }
- return false;
- }
- void CLocalWorkUnit::protect(bool protectMode)
- {
- CriticalBlock block(crit);
- p->setPropBool("@protected", protectMode);
- }
- void CLocalWorkUnit::setResultLimit(unsigned value)
- {
- CriticalBlock block(crit);
- p->setPropInt("resultLimit", value);
- }
- unsigned CLocalWorkUnit::getResultLimit() const
- {
- CriticalBlock block(crit);
- return p->getPropInt("resultLimit");
- }
- IStringVal & CLocalWorkUnit::getSnapshot(IStringVal & str) const
- {
- CriticalBlock block(crit);
- str.set(p->queryProp("SNAPSHOT"));
- return str;
- }
- void CLocalWorkUnit::setSnapshot(const char * val)
- {
- CriticalBlock block(crit);
- p->setProp("SNAPSHOT", val);
- }
- const static EnumMapping warningSeverityMap[] =
- {
- { SeverityInformation, "info" },
- { SeverityWarning, "warning" },
- { SeverityError, "error" },
- { SeverityAlert, "alert" },
- { SeverityIgnore, "ignore" },
- { SeverityFatal, "fatal" },
- { SeverityUnknown, NULL }
- };
- ErrorSeverity CLocalWorkUnit::getWarningSeverity(unsigned code, ErrorSeverity defaultSeverity) const
- {
- StringBuffer xpath;
- xpath.append("OnWarnings/OnWarning[@code='").append(code).append("']");
- CriticalBlock block(crit);
- IPropertyTree * mapping = p->queryPropTree(xpath);
- if (mapping)
- return (ErrorSeverity) getEnum(mapping, "@severity", warningSeverityMap);
- return defaultSeverity;
- }
- void CLocalWorkUnit::setWarningSeverity(unsigned code, ErrorSeverity severity)
- {
- StringBuffer xpath;
- xpath.append("OnWarnings/OnWarning[@code='").append(code).append("']");
- CriticalBlock block(crit);
- IPropertyTree * mapping = p->queryPropTree(xpath);
- if (!mapping)
- {
- IPropertyTree * onWarnings = ensurePTree(p, "OnWarnings");
- mapping = onWarnings->addPropTree("OnWarning");
- mapping->setPropInt("@code", code);
- }
- setEnum(mapping, "@severity", severity, warningSeverityMap);
- }
- static int comparePropTrees(IInterface * const *ll, IInterface * const *rr)
- {
- IPropertyTree *l = (IPropertyTree *) *ll;
- IPropertyTree *r = (IPropertyTree *) *rr;
- return stricmp(l->queryName(), r->queryName());
- };
- unsigned CLocalWorkUnit::calculateHash(unsigned crc)
- {
- // Any other values in the WU that could affect generated code should be crc'ed here
- IPropertyTree *tree = p->queryBranch("Debug");
- if (tree)
- {
- Owned<IPropertyTreeIterator> sub = tree->getElements("*");
- ICopyArrayOf<IPropertyTree> subs;
- for(sub->first(); sub->isValid(); sub->next())
- subs.append(sub->query());
- subs.sort(comparePropTrees);
- ForEachItemIn(idx, subs)
- {
- const char *name = subs.item(idx).queryName();
- const char *val = subs.item(idx).queryProp(NULL);
- crc = crc32(name, (size32_t)strlen(name), crc);
- if (val)
- crc = crc32(val, (size32_t)strlen(val), crc);
- }
- }
- Owned<IConstWUPluginIterator> plugins = &getPlugins();
- for (plugins->first();plugins->isValid();plugins->next())
- {
- IConstWUPlugin &thisplugin = plugins->query();
- SCMStringBuffer version;
- thisplugin.getPluginVersion(version);
- crc = crc32(version.str(), version.length(), crc);
- }
- return crc;
- }
- static void updateProp(IPropertyTree * to, const IPropertyTree * from, const char * xpath)
- {
- if (!to->hasProp(xpath) && from->hasProp(xpath))
- to->setProp(xpath, from->queryProp(xpath));
- }
- static void setProp(IPropertyTree * to, const IPropertyTree * from, const char * xpath)
- {
- if (from->hasProp(xpath))
- to->setProp(xpath, from->queryProp(xpath));
- }
- static void copyTree(IPropertyTree * to, const IPropertyTree * from, const char * xpath)
- {
- IPropertyTree * match = from->getBranch(xpath);
- if (match)
- to->setPropTree(xpath, match);
- }
- IPropertyTree *CLocalWorkUnit::queryPTree() const
- {
- return p;
- }
- void CLocalWorkUnit::copyWorkUnit(IConstWorkUnit *cached, bool copyStats, bool all)
- {
- CLocalWorkUnit *from = QUERYINTERFACE(cached, CLocalWorkUnit);
- if (!from)
- {
- CLockedWorkUnit *fl = QUERYINTERFACE(cached, CLockedWorkUnit);
- if (!fl)
- throw MakeStringException(WUERR_InternalUnknownImplementation, "Cached workunit not created using workunit dll");
- from = fl->c;
- }
- // Need to copy the query, the results, and the graphs from the cached query.
- // The cache is made before the query is executed so there is no need to clear them.
- if (!cached->getCloneable())
- throw MakeStringException(WUERR_CannotCloneWorkunit, "Source work unit not marked as clonable");
- const IPropertyTree * fromP = from->p;
- IPropertyTree *pt;
- CriticalBlock block(crit);
- clearCached(false);
- query.clear();
- updateProp(p, fromP, "@jobName");
- copyTree(p, fromP, "Query");
- pt = fromP->getBranch("Application");
- if (pt)
- synchronizePTree(ensurePTree(p, "Application"), pt, false, false);
- pt = fromP->queryBranch("Debug");
- if (pt)
- {
- IPropertyTree *curDebug = p->queryPropTree("Debug");
- if (curDebug)
- {
- Owned<IPropertyTreeIterator> elems = pt->getElements("*");
- ForEach(*elems)
- {
- IPropertyTree *elem = &elems->query();
- if (!curDebug->hasProp(elem->queryName()))
- curDebug->setPropTree(elem->queryName(),LINK(elem));
- }
- }
- else
- p->setPropTree("Debug", LINK(pt));
- }
- copyTree(p, fromP, "OnWarnings");
- copyTree(p, fromP, "Plugins");
- copyTree(p, fromP, "Libraries");
- copyTree(p, fromP, "Results");
- copyTree(p, fromP, "Graphs");
- copyTree(p, fromP, "Workflow");
- copyTree(p, fromP, "WebServicesInfo");
- if (copyStats)
- {
- // Merge timing info from both branches
- pt = fromP->getBranch("Statistics");
- if (pt)
- {
- IPropertyTree *tgtStatistics = ensurePTree(p, "Statistics");
- mergePTree(tgtStatistics, pt);
- pt->Release();
- }
- }
- updateProp(p, fromP, "@clusterName");
- updateProp(p, fromP, "allowedclusters");
- updateProp(p, fromP, "@submitID");
- updateProp(p, fromP, "SNAPSHOT");
- setProp(p, fromP, "@eventScheduledCount");
- //MORE: This is very adhoc. All options that should be cloned should really be in a common branch
- if (all)
- {
- setProp(p, fromP, "PriorityFlag");
- setProp(p, fromP, "@priorityClass");
- setProp(p, fromP, "@protected");
- setProp(p, fromP, "@clusterName");
- updateProp(p, fromP, "@scope");
- }
- //Variables may have been set up as parameters to the query - so need to preserve any values that were supplied.
- pt = fromP->getBranch("Variables");
- if (pt)
- {
- IPropertyTree *ptTgtVariables = ensurePTree(p, "Variables");
- Owned<IPropertyTreeIterator> ptiVariable = pt->getElements("Variable");
- for (ptiVariable->first(); ptiVariable->isValid(); ptiVariable->next())
- {
- IPropertyTree *ptSrcVariable = &ptiVariable->query();
- const char *name = ptSrcVariable->queryProp("@name");
- assertex(name);
- StringBuffer xpath;
- xpath.append("Variable[@name='").append(name).append("']");
- IPropertyTree *ptTgtVariable = ptTgtVariables->queryPropTree(xpath.str());
- IPropertyTree *merged = createPTreeFromIPT(ptSrcVariable); // clone entire source info...
- merged->removeProp("Value"); // except value and status
- merged->setProp("@status", "undefined");
- if (!merged->getPropBool("@isScalar"))
- merged->removeProp("totalRowCount");
- merged->removeProp("rowCount");
- // If there are any other fields that get set ONLY by eclagent, strip them out here...
- if (ptTgtVariable)
- {
- // copy status and Value from what is already set in target
- merged->setProp("@status", ptTgtVariable->queryProp("@status"));
- MemoryBuffer value;
- if (ptTgtVariable->getPropBin("Value", value))
- merged->setPropBin("Value", value.length(), value.toByteArray());
- ptTgtVariable->removeProp(xpath.str());
- // If there are any other fields in a variable that get set by ws_ecl before submitting, copy them across here...
- }
- ptTgtVariables->addPropTree("Variable", merged);
- }
- pt->Release();
- }
- p->setProp("@codeVersion", fromP->queryProp("@codeVersion"));
- p->setProp("@buildVersion", fromP->queryProp("@buildVersion"));
- p->setProp("@eclVersion", fromP->queryProp("@eclVersion"));
- p->setProp("@totalThorTime", fromP->queryProp("@totalThorTime"));
- p->setProp("@hash", fromP->queryProp("@hash"));
- p->setProp("@executeCost", fromP->queryProp("@executeCost"));
- p->setProp("@fileAccessCost", fromP->queryProp("@fileAccessCost"));
- p->setPropBool("@cloneable", true);
- p->setPropBool("@isClone", true);
- resetWorkflow(); // the source Workflow section may have had some parts already executed...
- Owned<IPropertyTreeIterator> results = p->getElements("Results/Result");
- ForEach(*results)
- {
- CLocalWUResult result(LINK(&results->query()));
- result.setResultStatus(ResultStatusUndefined);
- }
- copyTree(p, fromP, "usedsources"); // field usage
- }
- bool CLocalWorkUnit::hasDebugValue(const char *propname) const
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- return p->hasProp(prop.append(lower));
- }
- IStringVal& CLocalWorkUnit::getDebugValue(const char *propname, IStringVal &str) const
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- str.set(p->queryProp(prop.append(lower).str()));
- return str;
- }
- IStringIterator& CLocalWorkUnit::getDebugValues() const
- {
- return getDebugValues(NULL);
- }
- IStringIterator& CLocalWorkUnit::getDebugValues(const char *prop) const
- {
- CriticalBlock block(crit);
- StringBuffer path("Debug/");
- if (prop)
- {
- StringBuffer lower;
- lower.append(prop).toLowerCase();
- path.append(lower);
- }
- else
- path.append("*");
- return *new CStringPTreeTagIterator(p->getElements(path.str()));
- }
- int CLocalWorkUnit::getDebugValueInt(const char *propname, int defVal) const
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- prop.append(lower);
- return p->getPropInt(prop.str(), defVal);
- }
- __int64 CLocalWorkUnit::getDebugValueInt64(const char *propname, __int64 defVal) const
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- prop.append(lower);
- return p->getPropInt64(prop.str(), defVal);
- }
- double CLocalWorkUnit::getDebugValueReal(const char *propname, double defVal) const
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- prop.append(lower);
- return p->getPropReal(prop.str(), defVal);
- }
- bool CLocalWorkUnit::getDebugValueBool(const char * propname, bool defVal) const
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- prop.append(lower);
- return p->getPropBool(prop.str(), defVal);
- }
- IStringIterator *CLocalWorkUnit::getLogs(const char *type, const char *instance) const
- {
- VStringBuffer xpath("Process/%s/", type);
- if (instance)
- xpath.append(instance);
- else
- xpath.append("*");
- CriticalBlock block(crit);
- if (p->getPropInt("@wuidVersion") < 1) // legacy wuid
- {
- // NB: instance unused
- if (streq("EclAgent", type))
- return new CStringPTreeIterator(p->getElements("Debug/eclagentlog"));
- else if (streq("Thor", type))
- return new CStringPTreeIterator(p->getElements("Debug/thorlog*"));
- VStringBuffer xpath("Debug/%s", type);
- return new CStringPTreeIterator(p->getElements(xpath.str()));
- }
- else
- return new CStringPTreeAttrIterator(p->getElements(xpath.str()), "@log");
- }
- IPropertyTreeIterator* CLocalWorkUnit::getProcesses(const char *type, const char *instance) const
- {
- VStringBuffer xpath("Process/%s/", type);
- if (instance)
- xpath.append(instance);
- else
- xpath.append("*");
- CriticalBlock block(crit);
- return p->getElements(xpath.str());
- }
- IStringIterator *CLocalWorkUnit::getProcesses(const char *type) const
- {
- VStringBuffer xpath("Process/%s/*", type);
- CriticalBlock block(crit);
- return new CStringPTreeTagIterator(p->getElements(xpath.str()));
- }
- void CLocalWorkUnit::addProcess(const char *type, const char *instance, unsigned pid,
- unsigned max, const char *pattern, bool singleLog, const char *log)
- {
- VStringBuffer processType("Process/%s", type);
- VStringBuffer xpath("%s/%s", processType.str(), instance);
- if (log)
- xpath.appendf("[@log=\"%s\"]", log);
- CriticalBlock block(crit);
- if (!p->hasProp(xpath))
- {
- IPropertyTree *node = ensurePTree(p, processType.str());
- node = node->addPropTree(instance);
- node->setProp("@log", log);
- node->setPropInt("@pid", pid);
- if (max > 0)
- node->setPropInt("@max", max);
- if (!isEmptyString(pattern))
- node->setProp("@pattern", pattern);
- if (singleLog)
- node->setPropBool("@singleLog", true);
- }
- }
- void CLocalWorkUnit::setDebugValue(const char *propname, const char *value, bool overwrite)
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- prop.append(lower);
- if (overwrite || !p->hasProp(prop.str()))
- {
- // MORE - not sure this line should be needed....
- p->setProp("Debug", "");
- p->setProp(prop.str(), value);
- }
- }
- void CLocalWorkUnit::setDebugValueInt(const char *propname, int value, bool overwrite)
- {
- StringBuffer lower;
- lower.append(propname).toLowerCase();
- CriticalBlock block(crit);
- StringBuffer prop("Debug/");
- prop.append(lower);
- if (overwrite || !p->hasProp(prop.str()))
- {
- // MORE - not sure this line should be needed....
- p->setProp("Debug", "");
- p->setPropInt(prop.str(), value);
- }
- }
- void CLocalWorkUnit::setTracingValue(const char *propname, const char *value)
- {
- CriticalBlock block(crit);
- // MORE - not sure this line should be needed....
- p->setProp("Tracing", "");
- StringBuffer prop("Tracing/");
- p->setProp(prop.append(propname).str(), value);
- }
- void CLocalWorkUnit::setTracingValueInt(const char *propname, int value)
- {
- CriticalBlock block(crit);
- StringBuffer prop("Tracing/");
- p->setPropInt(prop.append(propname).str(), value);
- }
- void CLocalWorkUnit::setTracingValueInt64(const char *propname, __int64 value)
- {
- CriticalBlock block(crit);
- VStringBuffer prop("Tracing/%s", propname);
- p->setPropInt64(prop.str(), value);
- }
- IConstWUQuery* CLocalWorkUnit::getQuery() const
- {
- // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
- CriticalBlock block(crit);
- if (!query)
- {
- IPropertyTree *s = queryMergedTree()->getPropTree("Query");
- if (s)
- query.setown(new CLocalWUQuery(s)); // NB takes ownership of 's'
- }
- return query.getLink();
- }
- IWUQuery* CLocalWorkUnit::updateQuery()
- {
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- CriticalBlock block(crit);
- if (!query)
- {
- IPropertyTree *s = queryMergedTree()->queryPropTree("Query");
- if (!s)
- s = p->addPropTree("Query", createPTreeFromXMLString("<Query fetchEntire='1'/>")); // Is this really desirable (the fetchEntire) ?
- s->Link();
- query.setown(new CLocalWUQuery(s));
- }
- return query.getLink();
- }
- void CLocalWorkUnit::loadPlugins() const
- {
- CriticalBlock block(crit);
- if (!pluginsCached)
- {
- assertex(plugins.length() == 0);
- Owned<IPropertyTreeIterator> r = p->getElements("Plugins/Plugin");
- for (r->first(); r->isValid(); r->next())
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- plugins.append(*new CLocalWUPlugin(rp));
- }
- pluginsCached = true;
- }
- }
- IConstWUPluginIterator& CLocalWorkUnit::getPlugins() const
- {
- CriticalBlock block(crit);
- loadPlugins();
- return *new CArrayIteratorOf<IConstWUPlugin,IConstWUPluginIterator> (plugins, 0, (IConstWorkUnit *) this);
- }
- void CLocalWorkUnit::loadLibraries() const
- {
- CriticalBlock block(crit);
- if (!librariesCached)
- {
- assertex(libraries.length() == 0);
- Owned<IPropertyTreeIterator> r = p->getElements("Libraries/Library");
- ForEach(*r)
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- libraries.append(*new CLocalWULibrary(rp));
- }
- librariesCached = true;
- }
- }
- IConstWULibraryIterator& CLocalWorkUnit::getLibraries() const
- {
- CriticalBlock block(crit);
- loadLibraries();
- return *new CArrayIteratorOf<IConstWULibrary,IConstWULibraryIterator> (libraries, 0, (IConstWorkUnit *) this);
- }
- IConstWULibrary * CLocalWorkUnit::getLibraryByName(const char * search) const
- {
- CriticalBlock block(crit);
- loadLibraries();
- ForEachItemIn(idx, libraries)
- {
- SCMStringBuffer name;
- IConstWULibrary &cur = libraries.item(idx);
- cur.getName(name);
- if (stricmp(name.str(), search)==0)
- return &OLINK(cur);
- }
- return NULL;
- }
- StringBuffer &formatGraphTimerLabel(StringBuffer &str, const char *graphName, unsigned subGraphNum, unsigned __int64 subId)
- {
- str.append("Graph ").append(graphName);
- if (subGraphNum) str.append(" - ").append(subGraphNum).append(" (").append(subId).append(")");
- else if (subId) str.append(" - id(").append(subId).append(")");
- return str;
- }
- StringBuffer &formatGraphTimerScope(StringBuffer &str, unsigned wfid, const char *graphName, unsigned subGraphNum, unsigned __int64 subId)
- {
- if (wfid)
- str.append(WorkflowScopePrefix).append(wfid).append(":");
- str.append(graphName);
- if (subId) str.append(":sg").append(subId);
- return str;
- }
- bool parseGraphTimerLabel(const char *label, StringAttr &graphName, unsigned & graphNum, unsigned &subGraphNum, unsigned &subId)
- {
- // expects format: "Graph <graphname>[ - <subgraphnum> (<subgraphid>)]"
- unsigned len = (size32_t)strlen(label);
- if (len < 6 || (0 != memcmp(label, "Graph ", 6)))
- return false;
- graphNum = 0;
- subGraphNum = 0;
- subId = 0;
- const char *finger = label+6;
- const char *finger2 = strchr(finger, '-');
- if (NULL == finger2) // just graphName
- graphName.set(finger);
- else
- {
- graphName.set(finger, (size32_t)((finger2-1)-finger));
- finger = finger2+2; // skip '-' and space
- finger2 = strchr(finger, ' ');
- if (finger2)
- {
- subGraphNum = atoi_l(finger, (size32_t)(finger2-finger));
- finger = finger2+2; // skip space and '('
- finger2 = strchr(finger, ')');
- if (finger2)
- subId = atoi_l(finger, (size32_t)(finger2-finger));
- }
- else if (((len-(finger-label))>3) && 0 == memcmp(finger, "id(", 3)) // subgraph id only, new format.
- {
- finger += 3;
- finger2 = strchr(finger, ')');
- if (finger2)
- subId = atoi_l(finger, (size32_t)(finger2-finger));
- }
- }
- if (graphName && !memicmp(graphName, "graph", 5))
- graphNum = atoi(graphName + 5);
- return true;
- }
- bool parseGraphScope(const char *scope, StringAttr &graphName, unsigned & graphNum, unsigned &subGraphId)
- {
- if (!MATCHES_CONST_PREFIX(scope, GraphScopePrefix))
- return false;
- graphNum = atoi(scope + strlen(GraphScopePrefix));
- subGraphId = 0;
- const char * colon = strchr(scope, ':');
- if (!colon)
- {
- graphName.set(scope);
- return true;
- }
- const char * subgraph = colon+1;
- graphName.set(scope, (size32_t)(colon - scope));
- if (MATCHES_CONST_PREFIX(subgraph, SubGraphScopePrefix))
- subGraphId = atoi(subgraph+strlen(SubGraphScopePrefix));
- return true;
- }
- 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)
- {
- if (!scope) scope = GLOBAL_SCOPE;
- const char * kindName = queryStatisticName(kind);
- StatisticMeasure measure = queryMeasure(kind);
- //creator. scope and name must all be present, and must not contain semi colons.
- assertex(creator && scope);
- CriticalBlock block(crit);
- IPropertyTree * stats = p->queryPropTree("Statistics");
- if (!stats)
- stats = p->addPropTree("Statistics");
- IPropertyTree * statTree = NULL;
- if (mergeAction != StatsMergeAppend)
- {
- StringBuffer xpath;
- xpath.append("Statistic[@creator='").append(creator).append("'][@scope='").append(scope).append("'][@kind='").append(kindName).append("']");
- statTree = stats->queryPropTree(xpath.str());
- }
- if (!statTree)
- {
- /* NB: Sasha archive uses this structure directly
- * if it changes, the code in saarch.cpp needs updating
- */
- statTree = stats->addPropTree("Statistic");
- statTree->setProp("@creator", creator);
- statTree->setProp("@scope", scope);
- statTree->setProp("@kind", kindName);
- //These items are primarily here to facilitate filtering.
- statTree->setProp("@unit", queryMeasureName(measure));
- statTree->setProp("@c", queryCreatorTypeName(creatorType));
- statTree->setProp("@s", queryScopeTypeName(scopeType));
- statTree->setPropInt64("@ts", getTimeStampNowValue());
- if (optDescription)
- statTree->setProp("@desc", optDescription);
- if (statistics.cached)
- statistics.append(statTree); // links statTree
- mergeAction = StatsMergeAppend;
- }
- if (mergeAction != StatsMergeAppend) // RKC->GH Is this right??
- {
- unsigned __int64 oldValue = statTree->getPropInt64("@value", 0);
- unsigned __int64 oldCount = statTree->getPropInt64("@count", 0);
- unsigned __int64 oldMax = statTree->getPropInt64("@max", 0);
- if (oldMax < oldValue)
- oldMax = oldValue;
- statTree->setPropInt64("@value", mergeStatisticValue(oldValue, value, mergeAction));
- statTree->setPropInt64("@count", count + oldCount);
- if (maxValue > oldMax)
- statTree->setPropInt64("@max", maxValue);
- }
- else
- {
- statTree->setPropInt64("@value", value);
- statTree->setPropInt64("@count", count);
- if (maxValue)
- statTree->setPropInt64("@max", maxValue);
- else
- statTree->removeProp("@max");
- }
- //Whenever a graph time is updated recalculate the total time spent in thor, and save it
- if ((scopeType == SSTgraph) && (kind == StTimeElapsed))
- {
- _loadStatistics();
- stat_type totalTime = 0;
- ForEachItemIn(i, statistics)
- {
- IConstWUStatistic & cur = statistics.item(i);
- if ((cur.getScopeType() == SSTgraph) && (cur.getKind() == StTimeElapsed))
- totalTime += cur.getValue();
- }
- StringBuffer t;
- formatTimeCollatable(t, totalTime, false);
- p->setProp("@totalThorTime", t);
- }
- if (scopeType == SSTglobal)
- {
- if (kind == StCostExecute)
- p->setPropInt64("@costExecute", value);
- else if (kind == StCostFileAccess)
- p->setPropInt64("@costFileAccess", value);
- }
- }
- void CLocalWorkUnit::_loadStatistics() const
- {
- statistics.load(p,"Statistics/*");
- }
- bool CLocalWorkUnit::getStatistic(stat_type & value, const char * scope, StatisticKind kind) const
- {
- //MORE: Optimize this....
- WuScopeFilter filter;
- filter.addScope(scope).setIncludeNesting(0).addRequiredStat(kind).addOutputStatistic(kind).finishedFilter();
- Owned<IConstWUScopeIterator> stats = &getScopeIterator(filter);
- if (stats->first())
- return stats->getStat(kind, value);
- return false;
- }
- IConstWUScopeIterator & CLocalWorkUnit::getScopeIterator(const WuScopeFilter & filter) const
- {
- assertex(filter.isOptimized());
- WuScopeSourceFlags sources = filter.querySources();
- Owned<CompoundStatisticsScopeIterator> compoundIter = new CompoundStatisticsScopeIterator(filter);
- if (sources & SSFsearchGlobalStats)
- {
- CriticalBlock block(crit);
- statistics.loadBranch(p,"Statistics");
- Owned<IConstWUScopeIterator> localStats(new WorkUnitStatisticsScopeIterator(statistics, filter.queryIterFilter()));
- compoundIter->addIter(localStats);
- }
- if (sources & SSFsearchGraphStats)
- {
- const char * wuid = p->queryName();
- Owned<IConstWUScopeIterator> scopeIter(new CConstGraphProgressScopeIterator(wuid, filter.queryIterFilter(), filter.queryMinVersion()));
- compoundIter->addIter(scopeIter);
- }
- if (sources & SSFsearchGraph)
- {
- Owned<IConstWUScopeIterator> graphIter(new GraphScopeIterator(this, filter.queryIterFilter()));
- compoundIter->addIter(graphIter);
- }
- if (sources & SSFsearchWorkflow)
- {
- Owned<IConstWorkflowItemIterator> iter = getWorkflowItems();
- if (iter)
- {
- Owned<IConstWUScopeIterator> workflowIter(new WorkflowStatisticsScopeIterator(iter));
- compoundIter->addIter(workflowIter);
- }
- }
- if (sources & SSFsearchExceptions)
- {
- Owned<IConstWUScopeIterator> notesIter(new NotesIterator(this,filter.queryIterFilter()));
- compoundIter->addIter(notesIter);
- }
- return *compoundIter.getClear();
- }
- IWUPlugin* CLocalWorkUnit::updatePluginByName(const char *qname)
- {
- CriticalBlock block(crit);
- IConstWUPlugin *existing = getPluginByName(qname);
- if (existing)
- return (IWUPlugin *) existing;
- if (!plugins.length())
- p->addPropTree("Plugins");
- IPropertyTree *pl = p->queryPropTree("Plugins");
- IPropertyTree *s = pl->addPropTree("Plugin");
- s->Link();
- IWUPlugin* q = new CLocalWUPlugin(s);
- q->Link();
- plugins.append(*q);
- q->setPluginName(qname);
- return q;
- }
- IConstWUPlugin* CLocalWorkUnit::getPluginByName(const char *qname) const
- {
- CriticalBlock block(crit);
- loadPlugins();
- ForEachItemIn(idx, plugins)
- {
- SCMStringBuffer name;
- IConstWUPlugin &cur = plugins.item(idx);
- cur.getPluginName(name);
- if (stricmp(name.str(), qname)==0)
- {
- cur.Link();
- return &cur;
- }
- }
- return NULL;
- }
- IWULibrary* CLocalWorkUnit::updateLibraryByName(const char *qname)
- {
- CriticalBlock block(crit);
- IConstWULibrary *existing = getLibraryByName(qname);
- if (existing)
- return (IWULibrary *) existing;
- if (!libraries.length())
- p->addPropTree("Libraries");
- IPropertyTree *pl = p->queryPropTree("Libraries");
- IPropertyTree *s = pl->addPropTree("Library");
- s->Link();
- IWULibrary* q = new CLocalWULibrary(s);
- q->Link();
- libraries.append(*q);
- q->setName(qname);
- return q;
- }
- void CLocalWorkUnit::_loadExceptions() const
- {
- assertex(exceptions.length() == 0);
- Owned<IPropertyTreeIterator> r = p->getElements("Exceptions/Exception");
- for (r->first(); r->isValid(); r->next())
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- exceptions.append(*new CLocalWUException(rp));
- }
- }
- void CLocalWorkUnit::loadExceptions() const
- {
- CriticalBlock block(crit);
- if (!exceptionsCached)
- {
- _loadExceptions();
- exceptionsCached = true;
- }
- }
- IConstWUExceptionIterator& CLocalWorkUnit::getExceptions() const
- {
- CriticalBlock block(crit);
- loadExceptions();
- return *new CArrayIteratorOf<IConstWUException,IConstWUExceptionIterator> (exceptions, 0, (IConstWorkUnit *) this);
- }
- unsigned CLocalWorkUnit::getExceptionCount() const
- {
- CriticalBlock block(crit);
- loadExceptions();
- return exceptions.length();
- }
- void CLocalWorkUnit::clearExceptions(const char *source)
- {
- CriticalBlock block(crit);
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- loadExceptions();
- ForEachItemInRev(idx, exceptions)
- {
- IWUException &e = exceptions.item(idx);
- SCMStringBuffer s;
- e.getExceptionSource(s);
- if (source)
- {
- if (!strieq(s.s, source))
- continue;
- }
- else
- {
- if (strieq(s.s, "eclcc") || strieq(s.s, "eclccserver") || strieq(s.s, "eclserver") )
- break;
- }
- VStringBuffer xpath("Exceptions/Exception[@sequence='%d']", e.getSequence());
- p->removeProp(xpath);
- exceptions.remove(idx);
- }
- if (exceptions.length() == 0)
- p->removeProp("Exceptions");
- }
- IWUException* CLocalWorkUnit::createException()
- {
- CriticalBlock block(crit);
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- loadExceptions();
- if (!exceptions.length())
- p->addPropTree("Exceptions");
- IPropertyTree *r = p->queryPropTree("Exceptions");
- IPropertyTree *s = r->addPropTree("Exception");
- s->setPropInt("@sequence", exceptions.ordinality());
- IWUException* q = new CLocalWUException(LINK(s));
- exceptions.append(*LINK(q));
- Owned<IJlibDateTime> now = createDateTimeNow();
- SCMStringBuffer temp;
- now->getString(temp);
- q->setTimeStamp(temp.str());
- return q;
- }
- IConstWUWebServicesInfo* CLocalWorkUnit::getWebServicesInfo() const
- {
- // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
- CriticalBlock block(crit);
- if (!webServicesInfoCached)
- {
- assertex(!webServicesInfo);
- IPropertyTree *s = p->getPropTree("WebServicesInfo");
- if (s)
- webServicesInfo.setown(new CLocalWUWebServicesInfo(s)); // NB takes ownership of 's'
- webServicesInfoCached = true;
- }
- return webServicesInfo.getLink();
- }
- IWUWebServicesInfo* CLocalWorkUnit::updateWebServicesInfo(bool create)
- {
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- CriticalBlock block(crit);
- if (!webServicesInfoCached)
- {
- IPropertyTree *s = p->queryPropTree("WebServicesInfo");
- if (!s)
- {
- if (create)
- s = p->addPropTree("WebServicesInfo");
- else
- return NULL;
- }
- s->Link();
- webServicesInfo.setown(new CLocalWUWebServicesInfo(s));
- webServicesInfoCached = true;
- }
- return webServicesInfo.getLink();
- }
- static int compareResults(IInterface * const *ll, IInterface * const *rr)
- {
- CLocalWUResult *l = (CLocalWUResult *) *ll;
- CLocalWUResult *r = (CLocalWUResult *) *rr;
- return l->getResultSequence() - r->getResultSequence();
- }
- void CLocalWorkUnit::_loadResults() const
- {
- Owned<IPropertyTreeIterator> r = p->getElements("Results/Result");
- for (r->first(); r->isValid(); r->next())
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- results.append(*new CLocalWUResult(rp));
- }
- }
- void CLocalWorkUnit::loadResults() const
- {
- if (!resultsCached)
- {
- assertex(results.length() == 0);
- _loadResults();
- results.sort(compareResults);
- resultsCached = true;
- }
- }
- void CLocalWorkUnit::_loadVariables() const
- {
- Owned<IPropertyTreeIterator> r = p->getElements("Variables/Variable");
- for (r->first(); r->isValid(); r->next())
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- variables.append(*new CLocalWUResult(rp));
- }
- }
- void CLocalWorkUnit::loadVariables() const
- {
- if (!variablesCached)
- {
- assertex(variables.length() == 0);
- _loadVariables();
- variablesCached = true;
- }
- }
- void CLocalWorkUnit::_loadTemporaries() const
- {
- Owned<IPropertyTreeIterator> r = p->getElements("Temporaries/Variable");
- for (r->first(); r->isValid(); r->next())
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- temporaries.append(*new CLocalWUResult(rp));
- }
- }
- void CLocalWorkUnit::loadTemporaries() const
- {
- if (!temporariesCached)
- {
- assertex(temporaries.length() == 0);
- _loadTemporaries();
- temporariesCached = true;
- }
- }
- void CLocalWorkUnit::deleteTemporaries()
- {
- CriticalBlock block(crit);
- if (temporariesCached)
- {
- temporaries.kill();
- temporariesCached = false;
- }
- p->removeProp("Temporaries");
- }
- IWUResult* CLocalWorkUnit::createResult()
- {
- CriticalBlock block(crit);
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- loadResults();
- if (!results.length())
- p->addPropTree("Results");
- IPropertyTree *r = p->queryPropTree("Results");
- IPropertyTree *s = r->addPropTree("Result");
- s->Link();
- IWUResult* q = new CLocalWUResult(s);
- q->Link();
- results.append(*q);
- return q;
- }
- IWUResult* CLocalWorkUnit::updateResultByName(const char *qname)
- {
- CriticalBlock block(crit);
- IConstWUResult *existing = getResultByName(qname);
- if (existing)
- return (IWUResult *) existing;
- IWUResult* q = createResult();
- q->setResultName(qname);
- return q;
- }
- IWUResult* CLocalWorkUnit::updateResultBySequence(unsigned seq)
- {
- CriticalBlock block(crit);
- IConstWUResult *existing = getResultBySequence(seq);
- if (existing)
- return (IWUResult *) existing;
- IWUResult* q = createResult();
- q->setResultSequence(seq);
- return q;
- }
- IConstWUResultIterator& CLocalWorkUnit::getResults() const
- {
- CriticalBlock block(crit);
- loadResults();
- return *new CArrayIteratorOf<IConstWUResult,IConstWUResultIterator> (results, 0, (IConstWorkUnit *) this);
- }
- IConstWUResult* CLocalWorkUnit::getResultByName(const char *qname) const
- {
- CriticalBlock block(crit);
- loadResults();
- ForEachItemIn(idx, results)
- {
- SCMStringBuffer name;
- IConstWUResult &cur = results.item(idx);
- cur.getResultName(name);
- if (stricmp(name.str(), qname)==0)
- {
- cur.Link();
- return &cur;
- }
- }
- return NULL;
- }
- IConstWUResult* CLocalWorkUnit::getQueryResultByName(const char *qname) const
- {
- CriticalBlock block(crit);
- loadResults();
- ForEachItemIn(idx, results)
- {
- IConstWUResult &cur = results.item(idx);
- if (!isSpecialResultSequence(cur.getResultSequence()))
- {
- SCMStringBuffer name;
- cur.getResultName(name);
- if (stricmp(name.str(), qname)==0)
- {
- cur.Link();
- return &cur;
- }
- }
- }
- return NULL;
- }
- IConstWUResult* CLocalWorkUnit::getResultBySequence(unsigned seq) const
- {
- CriticalBlock block(crit);
- loadResults();
- ForEachItemIn(idx, results)
- {
- IConstWUResult &cur = results.item(idx);
- if (cur.getResultSequence() == seq)
- {
- cur.Link();
- return &cur;
- }
- }
- return NULL;
- }
- IConstWUResultIterator& CLocalWorkUnit::getVariables() const
- {
- CriticalBlock block(crit);
- loadVariables();
- return *new CArrayIteratorOf<IConstWUResult,IConstWUResultIterator> (variables, 0, (IConstWorkUnit *) this);
- }
- IConstWUResult* CLocalWorkUnit::getGlobalByName(const char *qname) const
- {
- CriticalBlock block(crit);
- if (strcmp(p->queryName(), GLOBAL_WORKUNIT)==0)
- return getVariableByName(qname);
- Owned <IWorkUnit> global = globalFactory->getGlobalWorkUnit(secMgr, secUser);
- return global->getVariableByName(qname);
- }
- IWUResult* CLocalWorkUnit::updateGlobalByName(const char *qname)
- {
- CriticalBlock block(crit);
- if (strcmp(p->queryName(), GLOBAL_WORKUNIT)==0)
- return updateVariableByName(qname);
- Owned <IWorkUnit> global = globalFactory->getGlobalWorkUnit(secMgr, secUser);
- return global->updateVariableByName(qname);
- }
- IConstWUResult* CLocalWorkUnit::getVariableByName(const char *qname) const
- {
- CriticalBlock block(crit);
- loadVariables();
- ForEachItemIn(idx, variables)
- {
- SCMStringBuffer name;
- IConstWUResult &cur = variables.item(idx);
- cur.getResultName(name);
- if (stricmp(name.str(), qname)==0)
- {
- cur.Link();
- return &cur;
- }
- }
- return NULL;
- }
- IConstWUResult* CLocalWorkUnit::getTemporaryByName(const char *qname) const
- {
- CriticalBlock block(crit);
- loadTemporaries();
- ForEachItemIn(idx, temporaries)
- {
- SCMStringBuffer name;
- IConstWUResult &cur = temporaries.item(idx);
- cur.getResultName(name);
- if (stricmp(name.str(), qname)==0)
- {
- cur.Link();
- return &cur;
- }
- }
- return NULL;
- }
- IConstWUResultIterator& CLocalWorkUnit::getTemporaries() const
- {
- CriticalBlock block(crit);
- loadTemporaries();
- return *new CArrayIteratorOf<IConstWUResult,IConstWUResultIterator> (temporaries, 0, (IConstWorkUnit *) this);
- }
- IWUResult* CLocalWorkUnit::updateTemporaryByName(const char *qname)
- {
- CriticalBlock block(crit);
- IConstWUResult *existing = getTemporaryByName(qname);
- if (existing)
- return (IWUResult *) existing;
- IPropertyTree *vars = (temporaries.length()) ? p->queryPropTree("Temporaries") : p->addPropTree("Temporaries");
- IPropertyTree *s = vars->addPropTree("Variable");
- s->Link();
- IWUResult* q = new CLocalWUResult(s);
- q->Link();
- temporaries.append(*q);
- q->setResultName(qname);
- q->setResultSequence(ResultSequenceInternal);
- return q;
- }
- IWUResult* CLocalWorkUnit::updateVariableByName(const char *qname)
- {
- CriticalBlock block(crit);
- IConstWUResult *existing = getVariableByName(qname);
- if (existing)
- return (IWUResult *) existing;
- if (!variables.length())
- p->addPropTree("Variables");
- IPropertyTree *vars = p->queryPropTree("Variables");
- IPropertyTree *s = vars->addPropTree("Variable");
- s->Link();
- IWUResult* q = new CLocalWUResult(s);
- q->Link();
- variables.append(*q);
- q->setResultName(qname);
- q->setResultSequence(ResultSequenceStored);
- return q;
- }
- void CLocalWorkUnit::deleteTempFiles(const char *graph, bool deleteOwned, bool deleteJobOwned)
- {
- CriticalBlock block(crit);
- IPropertyTree *files = p->queryPropTree("Files");
- if (!files) return;
- Owned<IPropertyTreeIterator> iter = files->getElements("File");
- ICopyArrayOf<IPropertyTree> toRemove;
- ForEach (*iter)
- {
- IPropertyTree &file = iter->query();
- WUFileKind fileKind = (WUFileKind) file.getPropInt("@kind", WUFileStandard);
- if(file.getPropBool("@temporary")) fileKind = WUFileTemporary; // @temporary, legacy check
- bool needDelete;
- switch(fileKind)
- {
- case WUFileTemporary:
- if(graph==NULL)
- needDelete = true;
- else
- {
- const char *graphOwner = file.queryProp("@graph");
- needDelete = ((graphOwner==NULL) || (strcmp(graph, graphOwner)==0));
- }
- break;
- case WUFileJobOwned:
- needDelete = ((graph==NULL) && deleteJobOwned);
- break;
- case WUFileOwned:
- needDelete = ((graph==NULL) && deleteOwned);
- break;
- default:
- needDelete = false;
- }
- if(needDelete)
- {
- const char *name = file.queryProp("@name");
- LOG(MCdebugProgress, unknownJob, "Removing workunit file %s from DFS", name);
- queryDistributedFileDirectory().removeEntry(name, queryUserDescriptor());
- toRemove.append(file);
- }
- }
- ForEachItemIn(r, toRemove) files->removeTree(&toRemove.item(r));
- }
- static void _noteFileRead(IDistributedFile *file, IPropertyTree *filesRead)
- {
- IDistributedSuperFile *super = file->querySuperFile();
- StringBuffer fname;
- file->getLogicalName(fname);
- if (fname.length())
- {
- StringBuffer path("File[@name=\"");
- path.append(fname).append("\"]");
- IPropertyTree *fileTree = filesRead->queryPropTree(path.str());
- if (fileTree)
- fileTree->setPropInt("@useCount", fileTree->getPropInt("@useCount")+1);
- else
- {
- StringBuffer cluster;
- file->getClusterName(0,cluster);
- fileTree = createPTree();
- fileTree->setProp("@name", fname.str());
- fileTree->setProp("@cluster", cluster.str());
- fileTree->setPropInt("@useCount", 1);
- fileTree = filesRead->addPropTree("File", fileTree);
- }
- if (super)
- {
- fileTree->setPropBool("@super", true);
- Owned<IDistributedFileIterator> iter = super->getSubFileIterator(false);
- ForEach (*iter)
- {
- IDistributedFile &file = iter->query();
- StringBuffer fname;
- file.getLogicalName(fname);
- fileTree->addPropTree("Subfile")->setProp("@name", fname.str());
- _noteFileRead(&file, filesRead);
- }
- }
- }
- }
- void CLocalWorkUnit::_loadFilesRead() const
- {
- // Nothing to do
- }
- void CLocalWorkUnit::noteFileRead(IDistributedFile *file)
- {
- if (file)
- {
- CriticalBlock block(crit);
- IPropertyTree *files = p->queryPropTree("FilesRead");
- if (!files)
- files = p->addPropTree("FilesRead");
- _noteFileRead(file, files);
- }
- }
- void CLocalWorkUnit::noteFieldUsage(IPropertyTree * fieldUsage)
- {
- if (fieldUsage)
- {
- CriticalBlock block(crit);
- p->addPropTree("usedsources", fieldUsage);
- }
- }
- void CLocalWorkUnit::_loadFilesWritten() const
- {
- // Nothing to do
- }
- static void addFile(IPropertyTree *files, const char *fileName, const char *cluster, unsigned usageCount, WUFileKind fileKind, const char *graphOwner)
- {
- StringBuffer path("File[@name=\"");
- path.append(fileName).append("\"]");
- if (cluster)
- path.append("[@cluster=\"").append(cluster).append("\"]");
- IPropertyTree *file = files->queryPropTree(path.str());
- if (file) files->removeTree(file);
- file = createPTree();
- file->setProp("@name", fileName);
- if (cluster)
- file->setProp("@cluster", cluster);
- if (graphOwner)
- file->setProp("@graph", graphOwner);
- file->setPropInt("@kind", (unsigned)fileKind);
- if (WUFileTemporary == fileKind)
- file->setPropInt("@usageCount", usageCount);
- files->addPropTree("File", file);
- }
- void CLocalWorkUnit::addFile(const char *fileName, StringArray *clusters, unsigned usageCount, WUFileKind fileKind, const char *graphOwner)
- {
- CriticalBlock block(crit);
- IPropertyTree *files = p->queryPropTree("Files");
- if (!files)
- files = p->addPropTree("Files");
- if (!clusters)
- ::addFile(files, fileName, NULL, usageCount, fileKind, graphOwner);
- else
- {
- ForEachItemIn(c, *clusters)
- ::addFile(files, fileName, clusters->item(c), usageCount, fileKind, graphOwner);
- }
- }
- void CLocalWorkUnit::releaseFile(const char *fileName)
- {
- StringBuffer path("File[@name=\"");
- path.append(fileName).append("\"]");
- CriticalBlock block(crit);
- IPropertyTree *files = p->queryPropTree("Files");
- if (!files) return;
- Owned<IPropertyTreeIterator> fiter = files->getElements(path.str());
- if (fiter->first())
- {
- while (true)
- {
- IPropertyTree *file = &fiter->query();
- unsigned usageCount = file->getPropInt("@usageCount");
- bool more = fiter->next();
- if (usageCount > 1)
- file->setPropInt("@usageCount", usageCount-1);
- else
- {
- StringAttr name(file->queryProp("@name"));
- files->removeTree(file);
- if (!name.isEmpty()&&(1 == usageCount))
- {
- if (queryDistributedFileDirectory().removeEntry(fileName, queryUserDescriptor()))
- LOG(MCdebugProgress, unknownJob, "Removed (released) file %s from DFS", name.get());
- }
- }
- if (!more)
- break;
- }
- }
- }
- void CLocalWorkUnit::clearGraphProgress() const
- {
- }
- void CLocalWorkUnit::resetBeforeGeneration()
- {
- CriticalBlock block(crit);
- //Remove all associated files
- Owned<IWUQuery> q = updateQuery();
- q->removeAssociatedFiles();
- //Remove any pre-existing workflow information
- workflowIterator.clear();
- p->removeProp("Workflow");
- }
- unsigned CLocalWorkUnit::queryFileUsage(const char *fileName) const
- {
- StringBuffer path("Files/File[@name=\"");
- path.append(fileName).append("\"]/@usageCount");
- CriticalBlock block(crit);
- return p->getPropInt(path.str());
- }
- IConstWUFileUsageIterator * CLocalWorkUnit::getFieldUsage() const
- {
- CriticalBlock block(crit);
- IPropertyTree* fieldUsageTree = p->queryPropTree("usedsources");
- if (!fieldUsageTree)
- return NULL;
- IPropertyTreeIterator* iter = fieldUsageTree->getElements("*");
- return new CConstWUFileUsageIterator(iter);
- }
- bool isFilenameResolved(StringBuffer& filename)
- {
- size32_t length = filename.length();
- // With current implementation, if filename is surrounded by single quotes, it means that the filename was resolved at compile time.
- if (filename.length() >= 2 && filename.charAt(0) == '\'' && filename.charAt(length-1) == '\'')
- return true;
- else
- return false;
- }
- bool CLocalWorkUnit::getFieldUsageArray(StringArray & filenames, StringArray & columnnames, const char * clusterName) const
- {
- bool scopeLoaded = false;
- StringBuffer defaultScope;
- Owned<IConstWUFileUsageIterator> files = getFieldUsage();
- if (!files)
- return false; // this query was not compiled with recordFieldUsage option.
- ForEach(*files)
- {
- Owned<IConstWUFileUsage> file = files->get();
- StringBuffer filename(file->queryName());
- size32_t length = filename.length();
- if (length == 0)
- throw MakeStringException(WUERR_InvalidFieldUsage, "Invalid FieldUsage found in WU. Cannot enforce view security.");
- StringBuffer normalizedFilename;
- // Two cases to handle:
- // 1. Filename was known at compile time, and is surrounded in single quotes (i.e. 'filename').
- // 2. Filename could not be resolved at compile time (i.e. filename is an input to a query),
- // and is a raw expression WITHOUT surrounding single quotes (i.e. STORED('input_filename')).
- if (isFilenameResolved(filename))
- {
- // filename cannot be empty (i.e. empty single quotes '')
- if (length == 2)
- throw MakeStringException(WUERR_InvalidFieldUsage, "Invalid FieldUsage found in WU. Cannot enforce view security.");
- // Remove surrounding single quotes
- StringAttr cleanFilename(filename.str()+1, length-2);
- // When a filename doesn't start with a tilde (~), it means scope is omitted and is relying on a default scope.
- // We need to load a default scope from config and prefix the filename with it.
- if (cleanFilename.str()[0] != '~')
- {
- // loading a default scope from config is expensive, and should be only done once and be reused later.
- if (!scopeLoaded)
- {
- //MORE: This should actually depend on the cluster that was active when the file was read!
- if (!resolveFilePrefix(defaultScope, clusterName))
- throw MakeStringException(WUERR_InvalidCluster, "Unknown cluster %s", clusterName);
- scopeLoaded = true;
- }
- normalizedFilename.append(defaultScope.str());
- normalizedFilename.append(cleanFilename.str());
- }
- else
- {
- normalizedFilename.append(cleanFilename);
- }
- }
- else
- {
- // When filename is an unresolved expression, simply treat the expression as a "non-existent" filename.
- // It will have an effect of this query accessing a non-existent filename, and will be denied access unconditionally.
- normalizedFilename.append(filename.str());
- }
- Owned<IConstWUFieldUsageIterator> fields = file->getFields();
- ForEach(*fields)
- {
- Owned<IConstWUFieldUsage> field = fields->get();
- filenames.append(normalizedFilename.str());
- columnnames.append(field->queryName());
- }
- }
- return true;
- }
- IPropertyTree *CLocalWorkUnit::getDiskUsageStats()
- {
- return p->getPropTree("DiskUsageStats");
- }
- void CLocalWorkUnit::addDiskUsageStats(__int64 _avgNodeUsage, unsigned _minNode, __int64 _minNodeUsage, unsigned _maxNode, __int64 _maxNodeUsage, __int64 _graphId)
- {
- IPropertyTree *stats = p->queryPropTree("DiskUsageStats");
- offset_t maxNodeUsage;
- if (stats)
- maxNodeUsage = stats->getPropInt64("@maxNodeUsage");
- else
- {
- stats = p->addPropTree("DiskUsageStats");
- maxNodeUsage = 0;
- }
- if ((offset_t)_maxNodeUsage > maxNodeUsage)
- {
- // record all details at time of max node usage.
- stats->setPropInt("@minNode", _minNode);
- stats->setPropInt("@maxNode", _maxNode);
- stats->setPropInt64("@minNodeUsage", _minNodeUsage);
- stats->setPropInt64("@maxNodeUsage", _maxNodeUsage);
- stats->setPropInt64("@graphId", _graphId);
- if (_avgNodeUsage)
- {
- unsigned _skewHi = (unsigned)((100 * (_maxNodeUsage-_avgNodeUsage))/_avgNodeUsage);
- unsigned _skewLo = (unsigned)((100 * (_avgNodeUsage-_minNodeUsage))/_avgNodeUsage);
- stats->setPropInt("@skewHi", _skewHi);
- stats->setPropInt("@skewLo", _skewLo);
- }
- }
- }
- IPropertyTreeIterator & CLocalWorkUnit::getFileIterator() const
- {
- CriticalBlock block(crit);
- _loadFilesWritten();
- return * p->getElements("Files/File");
- }
- IPropertyTreeIterator & CLocalWorkUnit::getFilesReadIterator() const
- {
- CriticalBlock block(crit);
- _loadFilesRead();
- return * p->getElements("FilesRead/File");
- }
- //=================================================================================================
- bool CLocalWorkUnit::switchThorQueue(const char *cluster, IQueueSwitcher *qs)
- {
- CriticalBlock block(crit);
- if (qs->isAuto()&&!getAllowAutoQueueSwitch())
- return false;
- const char * currentcluster = queryClusterName();
- const char *wuid = p->queryName();
- StringBuffer curqname;
- getClusterThorQueueName(curqname, currentcluster);
- void *qi = qs->getQ(curqname.str(),wuid);
- if (!qi)
- return false;
- setClusterName(cluster);
- StringBuffer newqname;
- getClusterThorQueueName(newqname, cluster);
- qs->putQ(newqname.str(),wuid,qi);
- return true;
- }
- //=================================================================================================
- IPropertyTree *CLocalWorkUnit::getUnpackedTree(bool includeProgress) const
- {
- Owned<IPropertyTree> ret = createPTreeFromIPT(queryMergedTree());
- Owned<IConstWUGraphIterator> graphIter = &getGraphs(GraphTypeAny);
- ForEach(*graphIter)
- {
- IConstWUGraph &graph = graphIter->query();
- Owned<IPropertyTree> graphTree = graph.getXGMMLTree(includeProgress, false);
- SCMStringBuffer gName;
- graph.getName(gName);
- StringBuffer xpath("Graphs/Graph[@name=\"");
- xpath.append(gName.s).append("\"]/xgmml");
- IPropertyTree *xgmml = ret->queryPropTree(xpath.str());
- if (xgmml) // don't know of any reason it shouldn't exist
- {
- xgmml->removeProp("graphBin");
- xgmml->setPropTree("graph", graphTree.getClear());
- }
- }
- return ret.getClear();
- }
- IPropertyTree *CLocalWorkUnit::queryMergedTree() const
- {
- if (indirectTree)
- return indirectTree;
- if (!p->hasProp("@clonedFromWorkunit"))
- return p;
- else
- {
- indirectTree.setown(createPTreeFromIPT(p));
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- Owned<IConstWorkUnit> donor = factory->openWorkUnit(p->queryProp("@clonedFromWorkunit"));
- if (donor)
- {
- copyTree(indirectTree, queryExtendedWU(donor)->queryPTree(), "Graphs");
- copyTree(indirectTree, queryExtendedWU(donor)->queryPTree(), "Query");
- }
- return indirectTree;
- }
- }
- void CLocalWorkUnit::_loadGraphs(bool heavy) const
- {
- Owned<IPropertyTreeIterator> iter = queryMergedTree()->getElements("Graphs/Graph");
- ForEach(*iter)
- {
- IPropertyTree &graph = iter->query();
- graphs.append(*new CLocalWUGraph(*this, LINK(&graph)));
- }
- }
- void CLocalWorkUnit::loadGraphs(bool heavy) const
- {
- if (graphsCached < (heavy ? 2 : 1))
- {
- graphs.kill();
- _loadGraphs(heavy);
- graphsCached = (heavy ? 2 : 1);
- }
- }
- EnumMapping graphTypes[] = {
- { GraphTypeAny, "unknown" },
- { GraphTypeProgress, "progress" },
- { GraphTypeEcl, "ECL" },
- { GraphTypeActivities, "activities" },
- { GraphTypeSubProgress, "subgraph" },
- { GraphTypeSize, NULL },
- };
- WUGraphType getGraphTypeFromString(const char* type)
- {
- return (WUGraphType) getEnum(type, graphTypes);
- }
- CLocalWUGraph::CLocalWUGraph(const CLocalWorkUnit &_owner, IPropertyTree *props) : p(props), owner(_owner)
- {
- wuidVersion = owner.getWuidVersion();
- }
- IStringVal& CLocalWUGraph::getName(IStringVal &str) const
- {
- str.set(p->queryProp("@name"));
- return str;
- }
- IStringVal& CLocalWUGraph::getLabel(IStringVal &str) const
- {
- if (wuidVersion >= 2)
- {
- str.set(p->queryProp("@label"));
- return str;
- }
- else
- {
- Owned<IPropertyTree> xgmml = getXGMMLTree(false, false);
- str.set(xgmml->queryProp("@label"));
- return str;
- }
- }
- WUGraphState CLocalWUGraph::getState() const
- {
- return owner.queryGraphState(p->queryProp("@name"));
- }
- IStringVal& CLocalWUGraph::getXGMML(IStringVal &str, bool mergeProgress, bool doFormatStats) const
- {
- Owned<IPropertyTree> xgmml = getXGMMLTree(mergeProgress, doFormatStats);
- if (xgmml)
- {
- StringBuffer x;
- toXML(xgmml, x);
- str.set(x.str());
- }
- return str;
- }
- unsigned CLocalWorkUnit::getGraphCount() const
- {
- CriticalBlock block(crit);
- if (queryMergedTree()->hasProp("Graphs"))
- {
- return queryMergedTree()->queryPropTree("Graphs")->numChildren();
- }
- return 0;
- }
- unsigned CLocalWorkUnit::getSourceFileCount() const
- {
- CriticalBlock block(crit);
- _loadFilesRead();
- if (p->hasProp("FilesRead"))
- {
- return p->queryPropTree("FilesRead")->numChildren();
- }
- return 0;
- }
- unsigned CLocalWorkUnit::getResultCount() const
- {
- CriticalBlock block(crit);
- if (p->hasProp("Results"))
- {
- return p->queryPropTree("Results")->numChildren();
- }
- return 0;
- }
- unsigned CLocalWorkUnit::getVariableCount() const
- {
- CriticalBlock block(crit);
- if (p->hasProp("Variables"))
- {
- return p->queryPropTree("Variables")->numChildren();
- }
- return 0;
- }
- unsigned CLocalWorkUnit::getApplicationValueCount() const
- {
- CriticalBlock block(crit);
- if (p->hasProp("Application"))
- {
- return p->queryPropTree("Application")->numChildren();
- }
- return 0;
- }
- StringBuffer &appendPTreeOpenTag(StringBuffer &s, IPropertyTree *tree, const char *name, unsigned indent, bool hidePasswords)
- {
- appendXMLOpenTag(s, name, NULL, false);
- Owned<IAttributeIterator> attrs = tree->getAttributes(true);
- if (attrs->first())
- {
- unsigned attributeindent = indent + (size32_t) strlen(name);
- unsigned count = attrs->count();
- bool doindent = false;
- ForEach(*attrs)
- {
- if (hidePasswords && streq(attrs->queryName(), "@token"))
- continue;
- #ifndef _DEBUG
- if (hidePasswords && streq(attrs->queryName(), "@distributedAccessToken"))
- continue;
- #endif
- if (doindent)
- s.append('\n').appendN(attributeindent, ' ');
- else if (count > 3)
- doindent = true;
- appendXMLAttr(s, attrs->queryName()+1, attrs->queryValue());
- }
- }
- s.append('>');
- return s;
- }
- IStringVal &CLocalWorkUnit::getXmlParams(IStringVal &str, bool hidePasswords) const
- {
- CriticalBlock block(crit);
- IPropertyTree *paramTree = p->queryPropTree("Parameters");
- if (!paramTree)
- return str;
- StringBuffer xml;
- if (!hidePasswords)
- toXML(paramTree, xml);
- else
- {
- appendPTreeOpenTag(xml.append(' '), paramTree, "Parameters", 0, false).append('\n');
- Owned<IPropertyTreeIterator> elems = paramTree->getElements("*");
- ForEach(*elems)
- {
- const char *paramname = elems->query().queryName();
- VStringBuffer xpath("Variables/Variable[@name='%s']/Format/@password", paramname);
- if (p->getPropBool(xpath))
- appendXMLTag(xml.append(" "), paramname, "***").append('\n');
- else
- toXML(&elems->query(), xml, 2);
- }
- appendXMLCloseTag(xml.append(' '), "Parameters").append('\n');
- }
- str.set(xml);
- return str;
- }
- const IPropertyTree *CLocalWorkUnit::getXmlParams() const
- {
- CriticalBlock block(crit);
- return p->getPropTree("Parameters");
- }
- void CLocalWorkUnit::setXmlParams(const char *params)
- {
- CriticalBlock block(crit);
- p->setPropTree("Parameters", createPTreeFromXMLString(params));
- }
- void CLocalWorkUnit::setXmlParams(IPropertyTree *tree)
- {
- CriticalBlock block(crit);
- p->setPropTree("Parameters", tree);
- }
- unsigned __int64 CLocalWorkUnit::getHash() const
- {
- CriticalBlock block(crit);
- return p->getPropInt64("@hash");
- }
- void CLocalWorkUnit::setHash(unsigned __int64 hash)
- {
- CriticalBlock block(crit);
- p->setPropInt64("@hash", hash);
- }
- // getGraphs / getGraphsMeta
- // These are basically the same except for the amount of preloading they do, and the type of the iterator they return...
- // If a type other than any is requested, a postfilter is needed.
- template <class T, class U> class CFilteredGraphIteratorOf : public CInterfaceOf<T>
- {
- WUGraphType type;
- Owned<T> base;
- bool match()
- {
- return base->query().getType()==type;
- }
- public:
- CFilteredGraphIteratorOf<T,U>(T *_base, WUGraphType _type)
- : base(_base), type(_type)
- {
- }
- bool first()
- {
- if (!base->first())
- return false;
- if (match())
- return true;
- return next();
- }
- bool next()
- {
- while (base->next())
- if (match())
- return true;
- return false;
- }
- virtual bool isValid()
- {
- return base->isValid();
- }
- U & query()
- {
- return base->query();
- }
- };
- IConstWUGraphMetaIterator& CLocalWorkUnit::getGraphsMeta(WUGraphType type) const
- {
- /* NB: this method should be 'cheap', loadGraphs() creates IConstWUGraph interfaces to the graphs
- * it does not actually pull the graph data. We only use IConstWUGraphMeta here, which never probes the xgmml.
- */
- CriticalBlock block(crit);
- loadGraphs(false);
- IConstWUGraphMetaIterator *giter = new CArrayIteratorOf<IConstWUGraph,IConstWUGraphMetaIterator> (graphs, 0, (IConstWorkUnit *) this);
- if (type!=GraphTypeAny)
- giter = new CFilteredGraphIteratorOf<IConstWUGraphMetaIterator, IConstWUGraphMeta>(giter,type);
- return *giter;
- }
- IConstWUGraphIterator& CLocalWorkUnit::getGraphs(WUGraphType type) const
- {
- CriticalBlock block(crit);
- loadGraphs(true);
- IConstWUGraphIterator *giter = new CArrayIteratorOf<IConstWUGraph,IConstWUGraphIterator> (graphs, 0, (IConstWorkUnit *) this);
- if (type!=GraphTypeAny)
- giter = new CFilteredGraphIteratorOf<IConstWUGraphIterator, IConstWUGraph>(giter, type);
- return *giter;
- }
- IConstWUGraph* CLocalWorkUnit::getGraph(const char *qname) const
- {
- CriticalBlock block(crit);
- VStringBuffer xpath("Graphs/Graph[@name='%s']", qname);
- // NOTE - this would go wrong if we had other graphs of same name but different type. Ignore for now.
- IPTree *graph = queryMergedTree()->queryPropTree(xpath);
- if (graph)
- return new CLocalWUGraph(*this, LINK(graph));
- return NULL;
- }
- void CLocalWorkUnit::createGraph(const char * name, const char *label, WUGraphType type, IPropertyTree *xgmml, unsigned wfid)
- {
- CriticalBlock block(crit);
- if (!graphs.length())
- p->addPropTree("Graphs");
- IPropertyTree *r = p->queryPropTree("Graphs");
- IPropertyTree *s = r->addPropTree("Graph");
- CLocalWUGraph *q = new CLocalWUGraph(*this, LINK(s));
- q->setName(name);
- q->setLabel(label);
- q->setType(type);
- q->setWfid(wfid);
- q->setXGMMLTree(xgmml);
- graphs.append(*q);
- }
- IConstWUGraphProgress *CLocalWorkUnit::getGraphProgress(const char *graphName) const
- {
- IPTree *graphProgress = p->queryPropTree("GraphProgress");
- if (graphProgress)
- {
- IPTree *progress = graphProgress->queryPropTree(graphName);
- if (progress)
- return new CConstGraphProgress(queryWuid(), graphName, progress);
- }
- return nullptr;
- }
- WUGraphState CLocalWorkUnit::queryGraphState(const char *graphName) const
- {
- throwUnexpected(); // Should only be used for persisted workunits
- }
- WUGraphState CLocalWorkUnit::queryNodeState(const char *graphName, WUGraphIDType nodeId) const
- {
- throwUnexpected(); // Should only be used for persisted workunits
- }
- void CLocalWorkUnit::setGraphState(const char *graphName, unsigned wfid, WUGraphState state) const
- {
- throwUnexpected(); // Should only be used for persisted workunits
- }
- void CLocalWorkUnit::setNodeState(const char *graphName, WUGraphIDType nodeId, WUGraphState state) const
- {
- throwUnexpected(); // Should only be used for persisted workunits
- }
- IWUGraphStats *CLocalWorkUnit::updateStats(const char *graphName, StatisticCreatorType creatorType, const char * creator, unsigned _wfid, unsigned subgraph, bool merge) const
- {
- return new CLocalWuGraphStats(LINK(p), creatorType, creator, _wfid, graphName, subgraph, merge);
- }
- void CLocalWUGraph::setName(const char *str)
- {
- p->setProp("@name", str);
- }
- void CLocalWUGraph::setLabel(const char *str)
- {
- p->setProp("@label", str);
- }
- void CLocalWUGraph::setWfid(unsigned wfid)
- {
- p->setPropInt("@wfid", wfid);
- }
- void CLocalWUGraph::setXGMML(const char *str)
- {
- setXGMMLTree(createPTreeFromXMLString(str,ipt_lowmem));
- }
- void CLocalWUGraph::setXGMMLTree(IPropertyTree *_graph)
- {
- assertex(strcmp(_graph->queryName(), "graph")==0);
- IPropertyTree *xgmml = p->setPropTree("xgmml");
- MemoryBuffer mb;
- _graph->serialize(mb);
- // Note - we could compress further but that would introduce compatibility concerns, so don't bother
- // Cassandra workunit code actually lzw compresses the parent anyway
- xgmml->setPropBin("graphBin", mb.length(), mb.toByteArray());
- graph.setown(_graph);
- }
- static void expandAttributes(IPropertyTree & targetNode, IPropertyTree & progressNode)
- {
- Owned<IAttributeIterator> aIter = progressNode.getAttributes();
- ForEach (*aIter)
- {
- const char *aName = aIter->queryName()+1;
- if (0 != stricmp("id", aName)) // "id" reserved.
- {
- IPropertyTree *att = targetNode.addPropTree("att");
- att->setProp("@name", aName);
- att->setProp("@value", aIter->queryValue());
- }
- }
- }
- void CLocalWUGraph::mergeProgress(IPropertyTree &rootNode, IPropertyTree &progressTree, const unsigned &progressV) const
- {
- IPropertyTree *graphNode = rootNode.queryPropTree("att/graph");
- if (!graphNode) return;
- unsigned nodeId = rootNode.getPropInt("@id");
- StringBuffer progressNodePath("node[@id=\"");
- progressNodePath.append(nodeId).append("\"]");
- IPropertyTree *progressNode = progressTree.queryPropTree(progressNodePath.str());
- if (progressNode)
- {
- expandAttributes(*graphNode, *progressNode);
- Owned<IPropertyTreeIterator> edges = progressNode->getElements("edge");
- ForEach (*edges)
- {
- IPropertyTree &edge = edges->query();
- StringBuffer edgePath("edge[@id=\"");
- edgePath.append(edge.queryProp("@id")).append("\"]");
- IPropertyTree *graphEdge = graphNode->queryPropTree(edgePath.str());
- if (graphEdge)
- {
- if (progressV < 1)
- mergePTree(graphEdge, &edge);
- else
- { // must translate to XGMML format
- expandAttributes(*graphEdge, edge);
- // 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)
- Owned<IPropertyTreeIterator> iter = edge.getElements("*");
- ForEach (*iter)
- {
- IPropertyTree &t = iter->query();
- IPropertyTree *att = graphEdge->addPropTree("att");
- att->setProp("@name", t.queryName());
- att->setProp("@value", t.queryProp(NULL));
- }
- }
- }
- }
- Owned<IPropertyTreeIterator> nodes = progressNode->getElements("node");
- ForEach (*nodes)
- {
- IPropertyTree &node = nodes->query();
- StringBuffer nodePath("node[@id=\"");
- nodePath.append(node.queryProp("@id")).append("\"]");
- IPropertyTree *_node = graphNode->queryPropTree(nodePath.str());
- if (_node)
- {
- if (progressV < 1)
- mergePTree(_node, &node);
- else
- { // must translate to XGMML format
- expandAttributes(*_node, node);
- }
- }
- }
- }
- Owned<IPropertyTreeIterator> iter = graphNode->getElements("node");
- ForEach (*iter)
- mergeProgress(iter->query(), progressTree, progressV);
- }
- IPropertyTree * CLocalWUGraph::getXGMMLTreeRaw() const
- {
- return p->getPropTree("xgmml");
- }
- IPropertyTree * CLocalWUGraph::getXGMMLTree(bool doMergeProgress, bool doFormatStats) const
- {
- if (!graph)
- {
- // NB: although graphBin introduced in wuidVersion==2,
- // daliadmin can retrospectively compress existing graphs, so need to check for all versions
- MemoryBuffer mb;
- if (p->getPropBin("xgmml/graphBin", mb))
- graph.setown(createPTree(mb, ipt_lowmem));
- else
- graph.setown(p->getBranch("xgmml/graph"));
- if (!graph)
- return NULL;
- }
- if (!doMergeProgress)
- return graph.getLink();
- else
- {
- Owned<IPropertyTree> copy = createPTreeFromIPT(graph, ipt_lowmem);
- Owned<IConstWUGraphProgress> progress = owner.getGraphProgress(p->queryProp("@name"));
- if (progress)
- {
- //MORE: Eventually this should directly access the new stats structure
- unsigned progressV = progress->queryFormatVersion();
- Owned<IPropertyTree> progressTree = progress->getProgressTree(doFormatStats);
- Owned<IPropertyTreeIterator> nodeIterator = copy->getElements("node");
- ForEach (*nodeIterator)
- mergeProgress(nodeIterator->query(), *progressTree, progressV);
- }
- return copy.getClear();
- }
- }
- WUGraphType CLocalWUGraph::getType() const
- {
- return (WUGraphType) getEnum(p, "@type", graphTypes);
- }
- IStringVal & CLocalWUGraph::getTypeName(IStringVal &str) const
- {
- str.set(p->queryProp("@type"));
- if (!str.length())
- str.set("unknown");
- return str;
- }
- unsigned CLocalWUGraph::getWfid() const
- {
- return p->getPropInt("@wfid", 0);
- }
- void CLocalWUGraph::setType(WUGraphType _type)
- {
- setEnum(p, "@type", _type, graphTypes);
- }
- //=================================================================================================
- EnumMapping queryFileTypes[] = {
- { FileTypeCpp, "cpp" },
- { FileTypeDll, "dll" },
- { FileTypeResText, "res" },
- { FileTypeHintXml, "hint" },
- { FileTypeXml, "xml" },
- { FileTypeLog, "log" },
- { FileTypeSize, NULL },
- };
- CLocalWUAssociated::CLocalWUAssociated(IPropertyTree *props) : p(props)
- {
- }
- WUFileType CLocalWUAssociated::getType() const
- {
- return (WUFileType)getEnum(p, "@type", queryFileTypes);
- }
- IStringVal & CLocalWUAssociated::getDescription(IStringVal & str) const
- {
- str.set(p->queryProp("@desc"));
- return str;
- }
- IStringVal & CLocalWUAssociated::getIp(IStringVal & str) const
- {
- str.set(p->queryProp("@ip"));
- return str;
- }
- IStringVal & CLocalWUAssociated::getName(IStringVal & str) const
- {
- str.set(p->queryProp("@filename"));
- return str;
- }
- IStringVal & CLocalWUAssociated::getNameTail(IStringVal & str) const
- {
- str.set(pathTail(p->queryProp("@filename")));
- return str;
- }
- unsigned CLocalWUAssociated::getCrc() const
- {
- return p->getPropInt("@crc", 0);
- }
- unsigned CLocalWUAssociated::getMinActivityId() const
- {
- return p->getPropInt("@minActivity", 0);
- }
- unsigned CLocalWUAssociated::getMaxActivityId() const
- {
- return p->getPropInt("@maxActivity", 0);
- }
- //=================================================================================================
- CLocalWUQuery::CLocalWUQuery(IPropertyTree *props) : p(props)
- {
- associatedCached = false;
- }
- EnumMapping queryTypes[] = {
- { QueryTypeUnknown, "unknown" },
- { QueryTypeEcl, "ECL" },
- { QueryTypeSql, "SQL" },
- { QueryTypeXml, "XML" },
- { QueryTypeAttribute, "Attribute" },
- { QueryTypeSize, NULL },
- };
- WUQueryType CLocalWUQuery::getQueryType() const
- {
- return (WUQueryType) getEnum(p, "@type", queryTypes);
- }
- void CLocalWUQuery::setQueryType(WUQueryType qt)
- {
- setEnum(p, "@type", qt, queryTypes);
- }
- IStringVal& CLocalWUQuery::getQueryText(IStringVal &str) const
- {
- const char *text = p->queryProp("Text");
- if (!text)
- text = p->queryProp("ShortText");
- str.set(text);
- return str;
- }
- IStringVal& CLocalWUQuery::getQueryShortText(IStringVal &str) const
- {
- const char * text = p->queryProp("ShortText");
- if (text)
- str.set(text);
- else
- {
- text = p->queryProp("Text");
- if (isArchiveQuery(text))
- {
- Owned<IPropertyTree> xml = createPTreeFromXMLString(text, ipt_caseInsensitive|ipt_lowmem);
- const char * path = xml->queryProp("Query/@attributePath");
- if (path)
- {
- IPropertyTree * resolved = resolveDefinitionInArchive(xml, path);
- if (resolved)
- str.set(resolved->queryProp(NULL));
- }
- else
- str.set(xml->queryProp("Query"));
- }
- else
- str.set(text);
- }
- return str;
- }
- bool CLocalWUQuery::isArchive() const
- {
- if (p->hasProp("@isArchive"))
- return p->getPropBool("@isArchive");
- const char *text = p->queryProp("Text");
- return isArchiveQuery(text);
- }
- IStringVal& CLocalWUQuery::getQueryName(IStringVal &str) const
- {
- str.set(p->queryProp("@name"));
- return str;
- }
- IStringVal & CLocalWUQuery::getQueryMainDefinition(IStringVal & str) const
- {
- str.set(p->queryProp("@main"));
- return str;
- }
- IStringVal& CLocalWUQuery::getQueryDllName(IStringVal &str) const
- {
- Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeDll, 0);
- if (entry)
- entry->getNameTail(str);
- return str;
- }
- IStringVal& CLocalWUQuery::getQueryCppName(IStringVal &str) const
- {
- Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeCpp, 0);
- if (entry)
- entry->getName(str);
- return str;
- }
- IStringVal& CLocalWUQuery::getQueryResTxtName(IStringVal &str) const
- {
- Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeResText, 0);
- if (entry)
- entry->getName(str);
- return str;
- }
- unsigned CLocalWUQuery::getQueryDllCrc() const
- {
- Owned<IConstWUAssociatedFile> entry = getAssociatedFile(FileTypeDll, 0);
- if (entry)
- return entry->getCrc();
- return 0;
- }
- void CLocalWUQuery::setQueryText(const char *text)
- {
- bool isArchive = isArchiveQuery(text);
- if (isArchive)
- {
- p->setProp("Text", text);
- Owned<IPropertyTree> xml = createPTreeFromXMLString(text, ipt_caseInsensitive|ipt_lowmem);
- const char * path = xml->queryProp("Query/@attributePath");
- if (path)
- {
- IPropertyTree * resolved = resolveDefinitionInArchive(xml, path);
- if (resolved)
- p->setProp("ShortText", resolved->queryProp(NULL));
- }
- else
- p->setProp("ShortText", xml->queryProp("Query"));
- }
- else
- {
- p->setProp("Text", text); // At some point in the future we may be able to remove this,
- // but as long as there may be new workunits compiled by old systems, we can't
- p->setProp("ShortText", text);
- }
- p->setPropBool("@isArchive", isArchive);
- if (isArchive)
- p->setPropBool("@hasArchive", true); //preserved if setQueryText is called multiple times. Should setting this be more explicit?
- }
- void CLocalWUQuery::setQueryName(const char *qname)
- {
- p->setProp("@name", qname);
- }
- void CLocalWUQuery::setQueryMainDefinition(const char * str)
- {
- p->setProp("@main", str);
- }
- void CLocalWUQuery::addAssociatedFile(WUFileType type, const char * name, const char * ip, const char * desc, unsigned crc, unsigned minActivity, unsigned maxActivity)
- {
- CriticalBlock block(crit);
- loadAssociated();
- StringBuffer xpath;
- xpath.append("Associated/File[@filename=\"").append(name).append("\"]");
- if (p->hasProp(xpath))
- return;
- if (!associated.length())
- p->addPropTree("Associated");
- IPropertyTree *pl = p->queryPropTree("Associated");
- IPropertyTree *s = pl->addPropTree("File");
- setEnum(s, "@type", type, queryFileTypes);
- s->setProp("@filename", name);
- s->setProp("@ip", ip);
- s->setProp("@desc", desc);
- if (crc)
- s->setPropInt("@crc", crc);
- if (minActivity)
- s->setPropInt("@minActivity", minActivity);
- if (maxActivity)
- s->setPropInt("@maxActivity", maxActivity);
- IConstWUAssociatedFile * q = new CLocalWUAssociated(LINK(s));
- associated.append(*q);
- }
- void CLocalWUQuery::removeAssociatedFile(WUFileType type, const char * name, const char * desc)
- {
- CriticalBlock block(crit);
- associatedCached = false;
- associated.kill();
- StringBuffer xpath;
- xpath.append("Associated/File");
- if (type)
- xpath.append("[@type=\"").append(getEnumText(type, queryFileTypes)).append("\"]");
- if (name)
- xpath.append("[@filename=\"").append(name).append("\"]");
- if (desc)
- xpath.append("[@desc=\"").append(desc).append("\"]");
- p->removeProp(xpath.str());
- }
- void CLocalWUQuery::removeAssociatedFiles()
- {
- associatedCached = false;
- associated.kill();
- p->removeProp("Associated");
- }
- IConstWUAssociatedFile * CLocalWUQuery::getAssociatedFile(WUFileType type, unsigned index) const
- {
- CriticalBlock block(crit);
- loadAssociated();
- ForEachItemIn(idx, associated)
- {
- CLocalWUAssociated &cur = static_cast<CLocalWUAssociated &>(associated.item(idx));
- if (cur.getType() == type)
- {
- if (index-- == 0)
- return &OLINK(cur);
- }
- }
- return NULL;
- }
- void CLocalWUQuery::addSpecialCaseAssociated(WUFileType type, const char * propname, unsigned crc) const
- {
- const char * name = p->queryProp(propname);
- if (name)
- {
- IPropertyTree *s = createPTree("File");
- setEnum(s, "@type", type, queryFileTypes);
- s->setProp("@filename", name);
- if (crc)
- s->setPropInt("@crc", crc);
- associated.append(*new CLocalWUAssociated(s));
- }
- }
- void CLocalWUQuery::loadAssociated() const
- {
- CriticalBlock block(crit);
- if (!associatedCached)
- {
- assertex(associated.length() == 0);
- addSpecialCaseAssociated(FileTypeDll, "DllName", p->getPropInt("DllCrc", 0));
- addSpecialCaseAssociated(FileTypeCpp, "CppName", 0);
- addSpecialCaseAssociated(FileTypeResText, "ResTxtName", 0);
- Owned<IPropertyTreeIterator> r = p->getElements("Associated/File");
- for (r->first(); r->isValid(); r->next())
- {
- IPropertyTree *rp = &r->query();
- rp->Link();
- associated.append(*new CLocalWUAssociated(rp));
- }
- associatedCached = true;
- }
- }
- IConstWUAssociatedFileIterator& CLocalWUQuery::getAssociatedFiles() const
- {
- CriticalBlock block(crit);
- loadAssociated();
- return *new CArrayIteratorOf<IConstWUAssociatedFile,IConstWUAssociatedFileIterator> (associated, 0, (IConstWUQuery *) this);
- }
- //========================================================================================
- CLocalWUWebServicesInfo::CLocalWUWebServicesInfo(IPropertyTree *props) : p(props)
- {
- }
- IStringVal& CLocalWUWebServicesInfo::getModuleName(IStringVal &str) const
- {
- str.set(p->queryProp("@module"));
- return str;
- }
- IStringVal& CLocalWUWebServicesInfo::getAttributeName(IStringVal &str) const
- {
- str.set(p->queryProp("@attribute"));
- return str;
- }
- IStringVal& CLocalWUWebServicesInfo::getDefaultName(IStringVal &str) const
- {
- str.set(p->queryProp("@defaultName"));
- return str;
- }
- unsigned CLocalWUWebServicesInfo::getWebServicesCRC() const
- {
- return (unsigned) p->getPropInt("@crc");
- }
- IStringVal& CLocalWUWebServicesInfo::getInfo(const char *name, IStringVal &str) const
- {
- if (!name)
- {
- StringBuffer ws_info;
- ws_info.appendf("<%s ", p->queryName());
- Owned<IAttributeIterator> attrs = p->getAttributes();
- for(attrs->first(); attrs->isValid(); attrs->next())
- {
- const char *name = attrs->queryName()+1;
- const char *value = attrs->queryValue();
- ws_info.appendf("%s='%s' ", name, value);
- }
- ws_info.append("> \n");
- Owned<IPropertyTreeIterator> info = p->getElements("*");
- ForEach(*info)
- {
- IPropertyTree &item = info->query();
- const char *name = item.queryName();
- if (name)
- {
- MemoryBuffer mb;
- bool isbin = p->isBinary(name);
- if (isbin)
- {
- p->getPropBin(name,mb);
- if (mb.length())
- {
- unsigned len = 0;
- mb.read(len);
- StringBuffer encodedString;
- StringBuffer val(len, (const char *) mb.readDirect(len));
- encodeXML(val, encodedString);
- ws_info.appendf("<%s>%s</%s>", name, encodedString.str(), name);
- }
- }
- else
- {
- StringBuffer tmp;
- toXML(&item, tmp);
- ws_info.append(tmp.str());
- }
- }
- }
- ws_info.appendf("</%s>", p->queryName());
- str.setLen(ws_info.str(), ws_info.length());
- }
- else
- {
- MemoryBuffer mb;
- p->getPropBin(name,mb);
- if (mb.length())
- {
- unsigned len;
- mb.read(len);
- str.setLen((const char *) mb.readDirect(len), len);
- }
- }
- return str;
- }
- IStringVal& CLocalWUWebServicesInfo::getText(const char *name, IStringVal &str) const
- {
- str.set(p->queryProp(name));
- return str;
- }
- void CLocalWUWebServicesInfo::setModuleName(const char *mname)
- {
- p->setProp("@module", mname);
- }
- void CLocalWUWebServicesInfo::setAttributeName(const char *aname)
- {
- p->setProp("@attribute", aname);
- }
- void CLocalWUWebServicesInfo::setDefaultName(const char *dname)
- {
- p->setProp("@defaultName", dname);
- }
- void CLocalWUWebServicesInfo::setWebServicesCRC(unsigned crc)
- {
- p->setPropInt("@crc", crc);
- }
- void CLocalWUWebServicesInfo::setInfo(const char *name, const char *info)
- {
- MemoryBuffer m;
- unsigned len = (size32_t)strlen(info);
- serializeLPString(len, info, m);
- p->setPropBin(name, m.length(), m.toByteArray());
- }
- void CLocalWUWebServicesInfo::setText(const char *name, const char *info)
- {
- p->setProp(name, info);
- }
- //========================================================================================
- CLocalWUResult::CLocalWUResult(IPropertyTree *props) : p(props)
- {
- }
- EnumMapping resultStatuses[] = {
- { ResultStatusUndefined, "undefined" },
- { ResultStatusCalculated, "calculated" },
- { ResultStatusSupplied, "supplied" },
- { ResultStatusFailed, "failed" },
- { ResultStatusPartial, "partial" },
- { ResultStatusSize, NULL }
- };
- WUResultStatus CLocalWUResult::getResultStatus() const
- {
- return (WUResultStatus ) getEnum(p, "@status", resultStatuses);
- }
- IStringVal& CLocalWUResult::getResultName(IStringVal &str) const
- {
- str.set(p->queryProp("@name"));
- return str;
- }
- int CLocalWUResult::getResultSequence() const
- {
- return p->getPropInt("@sequence", -1);
- }
- bool CLocalWUResult::isResultScalar() const
- {
- return p->getPropInt("@isScalar", 1) != 0;
- }
- bool findSize(int size, IntArray &sizes)
- {
- ForEachItemIn(idx, sizes)
- {
- if (sizes.item(idx)==size)
- return true;
- }
- return false;
- }
- void CLocalWUResult::getSchema(IArrayOf<ITypeInfo> &types, StringAttrArray &names, IStringVal * eclText) const
- {
- MemoryBuffer schema;
- p->getPropBin("SchemaRaw", schema);
- if (schema.length())
- {
- for (;;)
- {
- StringAttr name;
- schema.read(name);
- if (*schema.readDirect(0)==type_void)
- break;
- names.append(*new StringAttrItem(name));
- types.append(*deserializeType(schema)); // MORE - nested records!
- }
- schema.skip(1);
- if (schema.length() != schema.getPos())
- {
- unsigned eclLen;
- schema.read(eclLen);
- const char * schemaData = (const char *)schema.readDirect(eclLen);
- if (eclText)
- {
- eclText->setLen(schemaData, eclLen);
- if ((eclLen == 0) && names.ordinality())
- {
- const char * firstName = names.item(0).text;
- StringBuffer temp;
- temp.append("RECORD ");
- types.item(0).getECLType(temp);
- temp.append(" value{NAMED('").append(firstName).append("')}").append("; END;");
- eclText->set(temp.str());
- }
- }
- }
- }
- }
- void readRow(StringBuffer &out, MemoryBuffer &in, TypeInfoArray &types, StringAttrArray &names)
- {
- ForEachItemIn(idx, types)
- {
- StringAttrItem &name = names.item(idx);
- ITypeInfo &type = types.item(idx);
- unsigned size = type.getSize();
- switch(type.getTypeCode())
- {
- case type_data:
- if (size==UNKNOWN_LENGTH)
- {
- if (in.remaining() < sizeof(int))
- throw MakeStringException(WUERR_CorruptResult, "corrupt workunit information");
- in.read(size);
- }
- outputXmlData(size, in.readDirect(size), name.text, out);
- break;
- case type_string:
- if (size==UNKNOWN_LENGTH)
- {
- if (in.remaining() < sizeof(int))
- throw MakeStringException(WUERR_CorruptResult, "corrupt workunit information");
- in.read(size);
- }
- outputXmlString(size, (const char *) in.readDirect(size), name.text, out);
- break;
- case type_varstring:
- {
- if (size == UNKNOWN_LENGTH)
- size = (size32_t)strlen((const char *) in.readDirect(0))+1;
- const char * text = (const char *) in.readDirect(size);
- outputXmlString((size32_t)strlen(text), text, name.text, out);
- break;
- }
- case type_unicode:
- {
- unsigned len = type.getStringLen();
- if (size==UNKNOWN_LENGTH)
- in.read(len);
- outputXmlUnicode(len, (UChar const *) in.readDirect(len*2), name.text, out);
- }
- break;
- case type_utf8:
- {
- unsigned len = type.getStringLen();
- if (size==UNKNOWN_LENGTH)
- {
- in.read(len);
- size = rtlUtf8Size(len, in.readDirect(0));
- }
- outputXmlUtf8(len, (const char *) in.readDirect(size), name.text, out);
- }
- break;
- case type_qstring:
- {
- unsigned len = type.getStringLen();
- if (size==UNKNOWN_LENGTH)
- in.read(len);
- unsigned outlen;
- char *outstr;
- rtlQStrToStrX(outlen, outstr, len, (const char *) in.readDirect(rtlQStrSize(len)));
- outputXmlString(outlen, outstr, name.text, out);
- free(outstr);
- break;
- }
- case type_int:
- case type_swapint:
- if (type.isSigned())
- {
- const unsigned char *raw = (const unsigned char *) in.readDirect(size);
- unsigned __int64 cval8 = 0;
- //MORE: I think this is wrong - swapped doesn't mean little/big/
- if (type.isSwappedEndian())
- {
- unsigned idx = 0;
- if (raw[idx] & 0x80)
- cval8 = (__int64)-1;
- while (size--)
- cval8 = (cval8 << 8) | raw[idx++];
- }
- else
- {
- if (raw[size-1] & 0x80)
- cval8 = (__int64)-1;
- while (size--)
- cval8 = (cval8 << 8) | raw[size];
- }
- outputXmlInt((__int64) cval8, name.text, out);
- }
- else
- {
- const unsigned char *raw = (const unsigned char *) in.readDirect(size);
- unsigned __int64 cval8 = 0;
- if (type.isSwappedEndian())
- {
- unsigned idx = 0;
- while (size--)
- cval8 = (cval8 << 8) | raw[idx++];
- }
- else
- {
- while (size--)
- cval8 = (cval8 << 8) | raw[size];
- }
- outputXmlUInt(cval8, name.text, out);
- }
- break;
- case type_boolean:
- bool cvalb;
- in.read(cvalb);
- outputXmlBool(cvalb, name.text, out);
- break;
- case type_decimal:
- if (type.isSigned())
- outputXmlDecimal(in.readDirect(size), size, type.getPrecision(), name.text, out);
- else
- outputXmlUDecimal(in.readDirect(size), size, type.getPrecision(), name.text, out);
- break;
- case type_real:
- double cvald;
- switch(size)
- {
- case 4:
- float cvalf;
- in.read(cvalf);
- cvald = cvalf;
- break;
- case 8:
- in.read(cvald);
- break;
- default:
- throwUnexpected();
- }
- outputXmlReal(cvald, name.text, out);
- break;
- default:
- assertex(!"unexpected type in raw record");
- break;
- }
- }
- }
- IStringVal& CLocalWUResult::getResultXml(IStringVal &str, bool hidePassword) const
- {
- TypeInfoArray types;
- StringAttrArray names;
- getSchema(types, names);
- StringBuffer xml;
- const char * name = p->queryProp("@name");
- if (name)
- xml.appendf("<Dataset name=\'%s\'>\n", name);
- else
- xml.append("<Dataset>\n");
- if (hidePassword && p->getPropBool("Format/@password"))
- {
- xml.append(" <Row>");
- appendXMLTag(xml, name, "****");
- xml.append("</Row>\n");
- }
- else if (p->hasProp("Value"))
- {
- MemoryBuffer raw;
- p->getPropBin("Value", raw);
- unsigned __int64 numrows = getResultRowCount();
- while (numrows--)
- {
- xml.append(" <Row>");
- readRow(xml, raw, types, names);
- xml.append("</Row>\n");
- }
- }
- else if (p->hasProp("xmlValue"))
- {
- xml.append(" <Row>");
- appendXMLTag(xml, name, p->queryProp("xmlValue"));
- xml.append("</Row>\n");
- }
- xml.append("</Dataset>\n");
- str.set(xml.str());
- return str;
- }
- IProperties *CLocalWUResult::queryResultXmlns()
- {
- CriticalBlock block(crit);
- if (xmlns)
- return xmlns;
- xmlns.setown(createProperties());
- Owned<IAttributeIterator> it = p->getAttributes();
- unsigned prefixLen = strlen("@xmlns");
- ForEach(*it)
- {
- const char *name = it->queryName();
- if (!strncmp("@xmlns", name, prefixLen))
- {
- if (name[prefixLen]==':') //normal case
- xmlns->setProp(name+prefixLen+1, it->queryValue());
- else if (!name[prefixLen]) //special case, unprefixed namespace
- xmlns->setProp("xmlns", it->queryValue());
- }
- }
- return xmlns;
- }
- unsigned CLocalWUResult::getResultFetchSize() const
- {
- return p->getPropInt("fetchSize", 100);
- }
- __int64 CLocalWUResult::getResultTotalRowCount() const
- {
- return p->getPropInt64("totalRowCount", -1);
- }
- __int64 CLocalWUResult::getResultRowCount() const
- {
- return p->getPropInt64("rowCount", 0);
- }
- void CLocalWUResult::getResultDataset(IStringVal & ecl, IStringVal & defs) const
- {
- ecl.set(p->queryProp("datasetEcl"));
- defs.set(p->queryProp("datasetEclDefs"));
- }
- IStringVal& CLocalWUResult::getResultLogicalName(IStringVal & val) const
- {
- val.set(p->queryProp("logicalName"));
- return val;
- }
- IStringVal& CLocalWUResult::getResultKeyField(IStringVal & ecl) const
- {
- ecl.set(p->queryProp("keyField"));
- return ecl;
- }
- unsigned CLocalWUResult::getResultRequestedRows() const
- {
- return p->getPropInt("requestedRows", 1);
- }
- IStringVal& CLocalWUResult::getResultEclSchema(IStringVal & str) const
- {
- TypeInfoArray types;
- StringAttrArray names;
- getSchema(types, names, &str);
- return str;
- }
- IStringVal& CLocalWUResult::getResultRecordSizeEntry(IStringVal & str) const
- {
- str.set(p->queryProp("@recordSizeEntry"));
- return str;
- }
- IStringVal& CLocalWUResult::getResultTransformerEntry(IStringVal & str) const
- {
- str.set(p->queryProp("@transformerEntry"));
- return str;
- }
- __int64 CLocalWUResult::getResultRowLimit() const
- {
- return p->getPropInt64("@rowLimit");
- }
- IStringVal& CLocalWUResult::getResultFilename(IStringVal & str) const
- {
- str.set(p->queryProp("@tempFilename"));
- return str;
- }
- IStringVal& CLocalWUResult::getResultFieldOpt(const char *name, IStringVal &str) const
- {
- str.clear();
- if (!name || !*name)
- return str;
- IPropertyTree *format = p->queryPropTree("Format");
- if (!format)
- return str;
- VStringBuffer xpath("@%s", name);
- str.set(format->queryProp(xpath));
- return str;
- }
- void CLocalWUResult::getResultWriteLocation(IStringVal & _graph, unsigned & _activityId) const
- {
- _graph.set(p->queryProp("@graph"));
- _activityId = p->getPropInt("@activity", 0);
- }
- void CLocalWUResult::setResultStatus(WUResultStatus status)
- {
- setEnum(p, "@status", status, resultStatuses);
- if (status==ResultStatusUndefined)
- {
- p->removeProp("Value");
- p->removeProp("totalRowCount");
- p->removeProp("rowCount");
- p->removeProp("@format");
- p->removeProp("@tempFileNmae");
- p->removeProp("logicalName");
- }
- }
- void CLocalWUResult::setResultName(const char *s)
- {
- p->setProp("@name", s);
- }
- void CLocalWUResult::setResultSequence(unsigned seq)
- {
- p->setPropInt("@sequence", seq);
- }
- void CLocalWUResult::setResultSchemaRaw(unsigned size, const void *schema)
- {
- p->setPropBin("SchemaRaw", size, schema);
- }
- void CLocalWUResult::setResultXmlns(const char *prefix, const char *uri)
- {
- StringBuffer xpath("@xmlns");
- if (prefix && *prefix)
- xpath.append(':').append(prefix);
- p->setProp(xpath, uri);
- }
- void CLocalWUResult::setResultFieldOpt(const char *name, const char *value)
- {
- if (!name || !*name)
- return;
- IPropertyTree *format = ensurePTree(p, "Format");
- VStringBuffer xpath("@%s", name);
- format->setProp(xpath, value);
- }
- void CLocalWUResult::setResultWriteLocation(const char * _graph, unsigned _activityId)
- {
- p->setProp("@graph", _graph);
- p->setPropInt("@activity", _activityId);
- }
- void CLocalWUResult::setResultScalar(bool isScalar)
- {
- p->setPropInt("@isScalar", (int) isScalar);
- if (isScalar)
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultRaw(unsigned len, const void *data, WUResultFormat format)
- {
- p->setPropBin("Value", len, data);
- setResultStatus(ResultStatusSupplied);
- setResultFormat(format);
- }
- void CLocalWUResult::setResultFormat(WUResultFormat format)
- {
- switch (format)
- {
- case ResultFormatXml:
- p->setProp("@format","xml");
- break;
- case ResultFormatXmlSet:
- p->setProp("@format","xmlSet");
- break;
- case ResultFormatCsv:
- p->setProp("@format","csv");
- break;
- default:
- p->removeProp("@format");
- break;
- }
- }
- void CLocalWUResult::setResultXML(const char *val)
- {
- p->setProp("xmlValue", val);
- }
- void CLocalWUResult::addResultRaw(unsigned len, const void *data, WUResultFormat format)
- {
- p->appendPropBin("Value", len, data);
- setResultStatus(ResultStatusPartial);
- const char *existingFormat = p->queryProp("@format");
- const char *formatStr = NULL;
- switch (format)
- {
- case ResultFormatXml:
- formatStr = "xml";
- break;
- case ResultFormatXmlSet:
- formatStr = "xmlSet";
- break;
- case ResultFormatCsv:
- formatStr = "csv";
- break;
- default:
- existingFormat = nullptr;
- p->removeProp("@format");
- break;
- }
- if (format)
- {
- if (existingFormat)
- {
- if (0 != stricmp(formatStr, existingFormat))
- throw MakeStringException(WUERR_ResultFormatMismatch, "addResult format %s, does not match existing format %s", formatStr, existingFormat);
- }
- else
- p->setProp("@format", formatStr);
- }
- }
- void CLocalWUResult::setResultFetchSize(unsigned rows)
- {
- p->setPropInt("fetchSize", rows);
- }
- void CLocalWUResult::setResultTotalRowCount(__int64 rows)
- {
- p->setPropInt64("totalRowCount", rows);
- }
- void CLocalWUResult::setResultRowCount(__int64 rows)
- {
- p->setPropInt64("rowCount", rows);
- }
- void CLocalWUResult::setResultDataset(const char *ecl, const char *defs)
- {
- p->setProp("datasetEcl", ecl);
- p->setProp("datasetEclDefs", defs);
- }
- void CLocalWUResult::setResultLogicalName(const char *logicalName)
- {
- p->setProp("logicalName", logicalName);
- }
- void CLocalWUResult::setResultKeyField(const char *ecl)
- {
- p->setProp("keyField", ecl);
- }
- void CLocalWUResult::setResultRequestedRows(unsigned rows)
- {
- p->setPropInt("requestedRows", rows);
- }
- void CLocalWUResult::setResultRecordSizeEntry(const char * entry)
- {
- p->setProp("@recordSizeEntry", entry);
- }
- void CLocalWUResult::setResultTransformerEntry(const char * entry)
- {
- p->setProp("@transformerEntry", entry);
- }
- void CLocalWUResult::setResultRowLimit(__int64 value)
- {
- p->setPropInt64("@rowLimit", value);
- }
- void CLocalWUResult::setResultFilename(const char * name)
- {
- p->setProp("@tempFilename", name);
- }
- // MORE - it's an undetected error if we call getResult... of a type that does not match schema
- __int64 CLocalWUResult::getResultInt() const
- {
- __int64 result = 0;
- MemoryBuffer s;
- p->getPropBin("Value", s);
- if (s.length())
- s.read(result);
- else
- {
- // NOTE - we use this rather than getPropInt64 since it handles uint64 values up to MAX_UINT better (for our purposes)
- const char *val = p->queryProp("xmlValue");
- if (val)
- result = rtlStrToInt8(strlen(val), val);
- }
- return result;
- }
- bool CLocalWUResult::getResultBool() const
- {
- bool result = false;
- MemoryBuffer s;
- p->getPropBin("Value", s);
- if (s.length())
- s.read(result);
- else
- result = p->getPropBool("xmlValue");
- return result;
- }
- double CLocalWUResult::getResultReal() const
- {
- double result = 0;
- MemoryBuffer s;
- p->getPropBin("Value", s);
- if (s.length())
- s.read(result);
- else
- {
- const char *xmlVal = p->queryProp("xmlValue");
- if (xmlVal)
- result = atof(xmlVal);
- }
- return result;
- }
- void CLocalWUResult::getResultDecimal(void * val, unsigned len, unsigned precision, bool isSigned) const
- {
- MemoryBuffer s;
- p->getPropBin("Value", s);
- if (s.length())
- {
- assertex(s.length() == len);
- s.read(len, val);
- }
- else
- {
- const char *xmlVal = p->queryProp("xmlValue");
- if (xmlVal)
- {
- Decimal d;
- d.setString(strlen(xmlVal), xmlVal);
- if (isSigned)
- d.getDecimal(len, precision, val);
- else
- d.getUDecimal(len, precision, val);
- }
- else
- memset(val, 0, len);
- }
- }
- IStringVal& CLocalWUResult::getResultString(IStringVal & str, bool hidePassword) const
- {
- if (hidePassword && p->getPropBool("@password"))
- {
- str.set("****");
- return str;
- }
- MemoryBuffer s;
- p->getPropBin("Value", s);
- if (s.length())
- {
- unsigned len;
- s.read(len);
- str.setLen((const char *) s.readDirect(len), len);
- }
- else
- {
- p->getPropBin("xmlValue", s);
- if (p->isBinary("xmlValue"))
- str.setLen(s.toByteArray(), s.length());
- else
- {
- char *ascii = rtlUtf8ToVStr(rtlUtf8Length(s.length(), s.toByteArray()), s.toByteArray());
- str.set(ascii);
- rtlFree(ascii);
- }
- }
- return str;
- }
- WUResultFormat CLocalWUResult::getResultFormat() const
- {
- const char * format = p->queryProp("@format");
- if (!format)
- return ResultFormatRaw;
- else if (strcmp(format, "xml") == 0)
- return ResultFormatXml;
- else if (strcmp(format, "xmlSet") == 0)
- return ResultFormatXmlSet;
- else if (strcmp(format, "csv") == 0)
- return ResultFormatCsv;
- else
- throw MakeStringException(WUERR_InvalidResultFormat, "Unrecognised result format %s", format);
- }
- IDataVal& CLocalWUResult::getResultRaw(IDataVal & data, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const
- {
- MemoryBuffer s;
- p->getPropBin("Value", s);
- unsigned len = s.length();
- if (len)
- {
- WUResultFormat format = getResultFormat();
- if (format == ResultFormatXml || format == ResultFormatXmlSet)
- {
- if (!xmlTransformer)
- throw MakeStringException(WUERR_MissingFormatTranslator, "No transformer supplied to translate XML format result");
- xmlTransformer->transform(data, len, s.readDirect(len), format == ResultFormatXml);
- }
- else if (format == ResultFormatCsv)
- {
- if (!csvTransformer)
- throw MakeStringException(WUERR_MissingFormatTranslator, "No transformer supplied to translate Csv format result");
- csvTransformer->transform(data, len, s.readDirect(len), true);
- }
- else
- data.setLen(s.readDirect(len), len);
- }
- else
- data.clear();
- return data;
- }
- unsigned CLocalWUResult::getResultHash() const
- {
- MemoryBuffer s;
- p->getPropBin("Value", s);
- unsigned len = s.length();
- const byte * data = (const byte *)s.toByteArray();
- return ~hashc(data, len, ~0);
- }
- IDataVal& CLocalWUResult::getResultUnicode(IDataVal & data) const
- {
- MemoryBuffer s;
- p->getPropBin("Value", s);
- if (s.length())
- {
- unsigned len;
- s.read(len);
- data.setLen(s.readDirect(len*2), len*2);
- }
- else
- {
- StringBuffer utf8;
- if (p->getProp("xmlValue", utf8))
- {
- unsigned outlen;
- UChar *out;
- rtlUtf8ToUnicodeX(outlen, out, utf8.length(), utf8.str());
- data.setLen(out, outlen*2);
- rtlFree(out);
- }
- else
- data.clear();
- }
- return data;
- }
- __int64 CLocalWUResult::getResultRawSize(IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const
- {
- WUResultFormat format = getResultFormat();
- if (format == ResultFormatRaw)
- {
- //MORE: This should not load the whole property...
- MemoryBuffer s;
- p->getPropBin("Value", s);
- return s.length();
- }
- else
- {
- MemoryBuffer temp;
- MemoryBuffer2IDataVal adaptor(temp);
- getResultRaw(adaptor, xmlTransformer, csvTransformer);
- return temp.length();
- }
- }
- IDataVal& CLocalWUResult::getResultRaw(IDataVal & data, __int64 from, __int64 length, IXmlToRawTransformer * xmlTransformer, ICsvToRawTransformer * csvTransformer) const
- {
- WUResultFormat format = getResultFormat();
- if (format != ResultFormatRaw)
- {
- MemoryBuffer temp;
- MemoryBuffer2IDataVal adaptor(temp);
- getResultRaw(adaptor, xmlTransformer, csvTransformer);
- unsigned len = temp.length();
- if (from > len) from = len;
- if (from + length > len) length = len - from;
- data.setLen(temp.readDirect(len) + from, (size32_t)length);
- return data;
- }
- else
- {
- //MORE: This should not load the whole property, and should be different from the code above...
- MemoryBuffer s;
- p->getPropBin("Value", s);
- unsigned len = s.length();
- if (from > len) from = len;
- if (from + length > len) length = len - from;
- data.setLen(s.readDirect(len) + from, (size32_t)length);
- return data;
- }
- }
- bool CLocalWUResult::getResultIsAll() const
- {
- return p->getPropBool("@isAll", false);
- }
- // MORE - it's an undetected error if we call setResult... of a type that does not match schema
- void CLocalWUResult::setResultInt(__int64 val)
- {
- // Note: we always serialize scalar integer results as int8, and schema must reflect this
- MemoryBuffer m;
- serializeInt8(val, m);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultUInt(unsigned __int64 val)
- {
- setResultInt((__int64) val);
- }
- void CLocalWUResult::setResultReal(double val)
- {
- // Note: we always serialize scalar real results as real8, and schema must reflect this
- MemoryBuffer m;
- serializeReal8(val, m);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultBool(bool val)
- {
- MemoryBuffer m;
- serializeBool(val, m);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultString(const char *val, unsigned len)
- {
- // Note: we always serialize scalar strings with length prefix, and schema must reflect this
- MemoryBuffer m;
- serializeLPString(len, val, m);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultUnicode(const void *val, unsigned len)
- {
- // Note: we always serialize scalar strings with length prefix, and schema must reflect this
- MemoryBuffer m;
- m.append(len).append(len*2, val);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultData(const void *val, unsigned len)
- {
- // Note: we always serialize scalar data with length prefix, and schema must reflect this
- MemoryBuffer m;
- serializeLPString(len, (const char *)val, m);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultDecimal(const void *val, unsigned len)
- {
- // Note: serialized as data but with length known from schema
- MemoryBuffer m;
- serializeFixedData(len, val, m);
- p->setPropBin("Value", m.length(), m.toByteArray());
- setResultRowCount(1);
- setResultTotalRowCount(1);
- }
- void CLocalWUResult::setResultRow(unsigned len, const void * data)
- {
- p->setPropBin("Value", len, data);
- setResultRowCount(1);
- setResultTotalRowCount(1);
- setResultFormat(ResultFormatRaw);
- }
- void CLocalWUResult::setResultIsAll(bool value)
- {
- p->setPropBool("@isAll", value);
- }
- //==========================================================================================
- CLocalWUPlugin::CLocalWUPlugin(IPropertyTree *props) : p(props)
- {
- }
- IStringVal& CLocalWUPlugin::getPluginName(IStringVal &str) const
- {
- str.set(p->queryProp("@dllname"));
- return str;
- }
- IStringVal& CLocalWUPlugin::getPluginVersion(IStringVal &str) const
- {
- str.set(p->queryProp("@version"));
- return str;
- }
- void CLocalWUPlugin::setPluginName(const char *str)
- {
- p->setProp("@dllname", str);
- }
- void CLocalWUPlugin::setPluginVersion(const char *str)
- {
- p->setProp("@version", str);
- }
- //==========================================================================================
- CLocalWULibrary::CLocalWULibrary(IPropertyTree *props) : p(props)
- {
- }
- IStringVal& CLocalWULibrary::getName(IStringVal &str) const
- {
- str.set(p->queryProp("@name"));
- return str;
- }
- void CLocalWULibrary::setName(const char *str)
- {
- p->setProp("@name", str);
- }
- //==========================================================================================
- CLocalWUException::CLocalWUException(IPropertyTree *props) : p(props)
- {
- }
- IStringVal& CLocalWUException::getExceptionSource(IStringVal &str) const
- {
- str.set(p->queryProp("@source"));
- return str;
- }
- IStringVal& CLocalWUException::getExceptionMessage(IStringVal &str) const
- {
- str.set(p->queryProp(NULL));
- return str;
- }
- unsigned CLocalWUException::getExceptionCode() const
- {
- return p->getPropInt("@code", 0);
- }
- ErrorSeverity CLocalWUException::getSeverity() const
- {
- return (ErrorSeverity)p->getPropInt("@severity", SeverityError);
- }
- IStringVal & CLocalWUException::getTimeStamp(IStringVal & dt) const
- {
- dt.set(p->queryProp("@time"));
- return dt;
- }
- IStringVal & CLocalWUException::getExceptionFileName(IStringVal & str) const
- {
- str.set(p->queryProp("@filename"));
- return str;
- }
- unsigned CLocalWUException::getExceptionLineNo() const
- {
- return p->getPropInt("@row", 0);
- }
- unsigned CLocalWUException::getExceptionColumn() const
- {
- return p->getPropInt("@col", 0);
- }
- unsigned CLocalWUException::getActivityId() const
- {
- const char * scope = queryScope();
- if (scope)
- {
- const char * colon = strrchr(scope, ':');
- if (colon && hasPrefix(colon+1, ActivityScopePrefix, true))
- return atoi(colon+1+strlen(ActivityScopePrefix));
- }
- return p->getPropInt("@activity", 0);
- }
- unsigned CLocalWUException::getSequence() const
- {
- return p->getPropInt("@sequence", 0);
- }
- const char * CLocalWUException::queryScope() const
- {
- return p->queryProp("@scope");
- }
- unsigned CLocalWUException::getPriority() const
- {
- return p->getPropInt("@prio", 0);
- }
- void CLocalWUException::setExceptionSource(const char *str)
- {
- p->setProp("@source", str);
- }
- void CLocalWUException::setExceptionMessage(const char *str)
- {
- p->setProp(NULL, str);
- }
- void CLocalWUException::setExceptionCode(unsigned code)
- {
- p->setPropInt("@code", code);
- }
- void CLocalWUException::setSeverity(ErrorSeverity level)
- {
- p->setPropInt("@severity", level);
- }
- void CLocalWUException::setTimeStamp(const char *str)
- {
- p->setProp("@time", str);
- }
- void CLocalWUException::setExceptionFileName(const char *str)
- {
- p->setProp("@filename", str);
- }
- void CLocalWUException::setExceptionLineNo(unsigned r)
- {
- p->setPropInt("@row", r);
- }
- void CLocalWUException::setExceptionColumn(unsigned c)
- {
- p->setPropInt("@col", c);
- }
- void CLocalWUException::setActivityId(unsigned _id)
- {
- p->setPropInt("@activity", _id);
- }
- void CLocalWUException::setScope(const char * _scope)
- {
- p->setProp("@scope", _scope);
- }
- void CLocalWUException::setPriority(unsigned _priority)
- {
- p->setPropInt("@prio", _priority);
- }
- //==========================================================================================
- CLocalWUAppValue::CLocalWUAppValue(const IPropertyTree *_owner, const IPropertyTree *_props) : owner(_owner), props(_props)
- {
- }
- const char * CLocalWUAppValue::queryApplication() const
- {
- return owner->queryName();
- }
- const char * CLocalWUAppValue::queryName() const
- {
- return props->queryName();
- }
- const char * CLocalWUAppValue::queryValue() const
- {
- return props->queryProp(nullptr);
- }
- //==========================================================================================
- CLocalWUStatistic::CLocalWUStatistic(const IPropertyTree *props) : p(props)
- {
- }
- IStringVal & CLocalWUStatistic::getCreator(IStringVal & str) const
- {
- const char * creator = p->queryProp("@creator");
- str.set(creator);
- return str;
- }
- IStringVal & CLocalWUStatistic::getDescription(IStringVal & str, bool createDefault) const
- {
- const char * desc = p->queryProp("@desc");
- if (desc)
- {
- str.set(desc); // legacy and in case it is overridden
- }
- else if (createDefault)
- {
- StatisticKind kind = getKind();
- assertex(kind != StKindNone);
- const char * scope = p->queryProp("@scope");
- assertex(scope);
- //Clean up the format of the scope when converting it to a description
- StringBuffer descriptionText;
- if (isGlobalScope(scope))
- {
- const char * creator = p->queryProp("@creator");
- descriptionText.append(creator).append(":");
- queryLongStatisticName(descriptionText, kind);
- }
- else
- {
- for (;;)
- {
- char c = *scope++;
- if (!c)
- break;
- if (c == ':')
- descriptionText.append(": ");
- else
- descriptionText.append(c);
- }
- if (kind != StTimeElapsed)
- queryLongStatisticName(descriptionText.append(": "), kind);
- }
- str.set(descriptionText);
- }
- else
- str.clear();
- return str;
- }
- IStringVal & CLocalWUStatistic::getFormattedValue(IStringVal & str) const
- {
- StringBuffer formatted;
- formatStatistic(formatted, getValue(), getMeasure());
- str.set(formatted);
- return str;
- }
- StatisticCreatorType CLocalWUStatistic::getCreatorType() const
- {
- return queryCreatorType(p->queryProp("@c"), SCTnone);
- }
- StatisticScopeType CLocalWUStatistic::getScopeType() const
- {
- return queryScopeType(p->queryProp("@s"), SSTnone);
- }
- StatisticKind CLocalWUStatistic::getKind() const
- {
- return queryStatisticKind(p->queryProp("@kind"), StKindNone);
- }
- const char * CLocalWUStatistic::queryScope() const
- {
- const char * scope = p->queryProp("@scope");
- if (!scope || streq(scope, LEGACY_GLOBAL_SCOPE))
- scope = GLOBAL_SCOPE;
- return scope;
- }
- StatisticMeasure CLocalWUStatistic::getMeasure() const
- {
- return queryMeasure(p->queryProp("@unit"), SMeasureNone);
- }
- unsigned __int64 CLocalWUStatistic::getValue() const
- {
- return p->getPropInt64("@value", 0);
- }
- unsigned __int64 CLocalWUStatistic::getCount() const
- {
- return p->getPropInt64("@count", 0);
- }
- unsigned __int64 CLocalWUStatistic::getMax() const
- {
- return p->getPropInt64("@max", 0);
- }
- unsigned __int64 CLocalWUStatistic::getTimestamp() const
- {
- return p->getPropInt64("@ts", 0);
- }
- //==========================================================================================
- extern WORKUNIT_API ILocalWorkUnit * createLocalWorkUnit(const char *xml)
- {
- Owned<CLocalWorkUnit> cw = new CLocalWorkUnit((ISecManager *) NULL, NULL);
- if (xml)
- cw->loadPTree(createPTreeFromXMLString(xml, ipt_lowmem));
- else
- {
- Owned<IPropertyTree> p = createPTree("W_LOCAL", ipt_lowmem);
- p->setProp("@xmlns:xsi", "http://www.w3.org/1999/XMLSchema-instance");
- cw->loadPTree(p.getClear());
- }
- ILocalWorkUnit* ret = QUERYINTERFACE(&cw->lockRemote(false), ILocalWorkUnit);
- return ret;
- }
- extern WORKUNIT_API ILocalWorkUnit * createLocalWorkUnitFromPTree(IPropertyTree *ptree)
- {
- Owned<CLocalWorkUnit> cw = new CLocalWorkUnit((ISecManager *) NULL, NULL);
- cw->loadPTree(ptree);
- ILocalWorkUnit* ret = QUERYINTERFACE(&cw->lockRemote(false), ILocalWorkUnit);
- return ret;
- }
- void exportWorkUnitToXMLWithHiddenPasswords(IPropertyTree *p, IIOStream &out, unsigned extraXmlFlags)
- {
- const char *name = p->queryName();
- if (!name)
- name = "__unnamed__";
- StringBuffer temp;
- writeStringToStream(out, appendPTreeOpenTag(temp, p, name, 1, true));
- Owned<IPropertyTreeIterator> elems = p->getElements("*", iptiter_sort);
- ForEach(*elems)
- {
- IPropertyTree &elem = elems->query();
- if (streq(elem.queryName(), "Parameters"))
- {
- writeStringToStream(out, appendPTreeOpenTag(temp.clear().append(' '), &elem, "Parameters", 2, false).append('\n'));
- Owned<IPropertyTreeIterator> params = elem.getElements("*", iptiter_sort);
- ForEach(*params)
- {
- IPropertyTree ¶m = params->query();
- const char *paramname = param.queryName();
- VStringBuffer xpath("Variables/Variable[@name='%s']/Format/@password", paramname);
- if (p->getPropBool(xpath))
- writeStringToStream(out, appendXMLTag(temp.clear().append(" "), paramname, "****").append('\n'));
- else
- {
- toXML(¶m, out, 2, XML_Format|XML_SortTags|extraXmlFlags);
- }
- }
- writeStringToStream(out, appendXMLCloseTag(temp.clear().append(' '), "Parameters").append('\n'));
- }
- else if (streq(elem.queryName(), "Variables"))
- {
- writeStringToStream(out, appendPTreeOpenTag(temp.clear().append(' '), &elem, "Variables", 2, false).append('\n'));
- Owned<IPropertyTreeIterator> vars = elem.getElements("*", iptiter_sort);
- ForEach(*vars)
- {
- Owned<IPropertyTree> var = LINK(&vars->query());
- if (var->getPropBool("Format/@password"))
- {
- var.setown(createPTreeFromIPT(var)); //copy and remove password values
- var->removeProp("Value");
- var->removeProp("xmlValue");
- }
- toXML(var, out, 2, XML_Format|XML_SortTags|extraXmlFlags);
- }
- writeStringToStream(out, appendXMLCloseTag(temp.clear().append(' '), "Variables").append('\n'));
- }
- else
- toXML(&elem, out, 1, XML_Format|XML_SortTags|extraXmlFlags);
- }
- writeStringToStream(out, appendXMLCloseTag(temp.clear(), name));
- }
- StringBuffer &exportWorkUnitToXMLWithHiddenPasswords(IPropertyTree *p, StringBuffer &str)
- {
- class CAdapter : public CInterface, implements IIOStream
- {
- StringBuffer &out;
- public:
- IMPLEMENT_IINTERFACE;
- CAdapter(StringBuffer &_out) : out(_out) { }
- virtual void flush() { }
- virtual size32_t read(size32_t len, void * data) { UNIMPLEMENTED; return 0; }
- virtual size32_t write(size32_t len, const void * data) { out.append(len, (const char *)data); return len; }
- } adapter(str);
- exportWorkUnitToXMLWithHiddenPasswords(p->queryBranch(NULL), adapter, 0);
- return str;
- }
- void exportWorkUnitToXMLFileWithHiddenPasswords(IPropertyTree *p, const char *filename, unsigned extraXmlFlags)
- {
- OwnedIFile ifile = createIFile(filename);
- OwnedIFileIO ifileio = ifile->open(IFOcreate);
- Owned<IIOStream> stream = createIOStream(ifileio);
- exportWorkUnitToXMLWithHiddenPasswords(p->queryBranch(NULL), *stream, extraXmlFlags);
- }
- extern WORKUNIT_API StringBuffer &exportWorkUnitToXML(const IConstWorkUnit *wu, StringBuffer &str, bool unpack, bool includeProgress, bool hidePasswords)
- {
- // MORE - queryPTree isn't really safe without holding CLocalWorkUnit::crit - really need to move these functions into CLocalWorkunit
- const IExtendedWUInterface *ewu = queryExtendedWU(wu);
- if (ewu)
- {
- Linked<IPropertyTree> p;
- if (unpack||includeProgress)
- p.setown(ewu->getUnpackedTree(includeProgress));
- else
- p.set(ewu->queryPTree());
- if (hidePasswords)
- return exportWorkUnitToXMLWithHiddenPasswords(p, str);
- else
- return toXML(p, str, 0, XML_Format|XML_SortTags);
- }
- else
- return str.append("Unrecognized workunit format");
- }
- extern WORKUNIT_API void exportWorkUnitToXMLFile(const IConstWorkUnit *wu, const char * filename, unsigned extraXmlFlags, bool unpack, bool includeProgress, bool hidePasswords, bool regressionTest)
- {
- const IExtendedWUInterface *ewu = queryExtendedWU(wu);
- if (ewu)
- {
- Linked<IPropertyTree> p;
- if (unpack||includeProgress)
- p.setown(ewu->getUnpackedTree(includeProgress));
- else if (regressionTest)
- p.setown(createPTreeFromIPT(ewu->queryPTree()));
- else
- p.set(ewu->queryPTree());
- if (hidePasswords)
- return exportWorkUnitToXMLFileWithHiddenPasswords(p, filename, extraXmlFlags);
- if (regressionTest)
- {
- //This removes any items from the xml that will vary from run to run, so they can be binary compared from run to run
- //The following attributes change with the build. Simpler to remove rather than needing to updated each build
- p->removeProp("@buildVersion");
- p->removeProp("@eclVersion");
- p->removeProp("@hash");
- //Remove statistics, and extract them to a separate file
- IPropertyTree * stats = p->queryPropTree("Statistics");
- if (stats)
- {
- StringBuffer statsFilename;
- statsFilename.append(filename).append(".stats");
- saveXML(statsFilename, stats, 0, (XML_Format|XML_SortTags|extraXmlFlags) & ~XML_LineBreakAttributes);
- p->removeProp("Statistics");
- }
- //Now remove timestamps from exceptions
- Owned<IPropertyTreeIterator> elems = p->getElements("Exceptions/Exception", iptiter_sort);
- ForEach(*elems)
- {
- IPropertyTree &elem = elems->query();
- elem.removeProp("@time");
- }
- }
- saveXML(filename, p, 0, XML_Format|XML_SortTags|extraXmlFlags);
- }
- else
- throw makeStringException(0, "Unrecognized workunit format");
- }
- extern WORKUNIT_API void submitWorkUnit(const char *wuid, const char *username, const char *password)
- {
- MemoryBuffer buffer;
- Owned<INamedQueueConnection> conn = createNamedQueueConnection(0); // MORE - security token?
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- Owned<IConstWorkUnit> cw = factory->openWorkUnit(wuid);
- assertex(cw);
- StringAttr clusterName(cw->queryClusterName());
- cw.clear();
- if (!clusterName.length())
- throw MakeStringException(WUERR_InvalidCluster, "No target cluster specified");
- StringBuffer serverQueue;
- getClusterEclCCServerQueueName(serverQueue, clusterName);
- assertex(serverQueue.length());
- Owned<IJobQueue> queue = createJobQueue(serverQueue.str());
- if (!queue.get())
- throw MakeStringException(WUERR_InvalidQueue, "Could not create workunit queue");
- IJobQueueItem *item = createJobQueueItem(wuid);
- queue->enqueue(item);
- }
- extern WORKUNIT_API void abortWorkUnit(const char *wuid)
- {
- StringBuffer xpath("/WorkUnitAborts/");
- xpath.append(wuid);
- Owned<IRemoteConnection> acon = querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE, SDS_LOCK_TIMEOUT);
- acon->queryRoot()->setPropInt(NULL, 1);
- }
- extern WORKUNIT_API void secSubmitWorkUnit(const char *wuid, ISecManager &secmgr, ISecUser &secuser)
- {
- if (checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Write, "Submit", true, true))
- submitWorkUnit(wuid, secuser.getName(), secuser.credentials().getPassword());
- }
- extern WORKUNIT_API void secAbortWorkUnit(const char *wuid, ISecManager &secmgr, ISecUser &secuser)
- {
- if (!checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Write, "Submit", true, true))
- return;
- abortWorkUnit(wuid);
- Owned<IConstWorkUnit> cw = globalFactory->openWorkUnit(wuid);
- if(!cw)
- return;
- WorkunitUpdate wu(&cw->lock());
- const char *abortBy = secuser.getName();
- if (abortBy && *abortBy)
- wu->setTracingValue("AbortBy", abortBy);
- wu->setTracingValueInt64("AbortTimeStamp", getTimeStampNowValue());
- }
- extern WORKUNIT_API void submitWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- if (secmgr && secuser)
- return secSubmitWorkUnit(wuid, *secmgr, *secuser);
- if (secuser)
- return submitWorkUnit(wuid, secuser->getName(), secuser->credentials().getPassword());
- submitWorkUnit(wuid, "", "");
- }
- extern WORKUNIT_API void abortWorkUnit(const char *wuid, ISecManager *secmgr, ISecUser *secuser)
- {
- if (secmgr && secuser)
- return secAbortWorkUnit(wuid, *secmgr, *secuser);
- abortWorkUnit(wuid);
- }
- bool CLocalWorkUnit::hasWorkflow() const
- {
- return p->hasProp("Workflow");
- }
- unsigned CLocalWorkUnit::queryEventScheduledCount() const
- {
- CriticalBlock block(crit);
- if (p->hasProp("@eventScheduledCount"))
- return p->getPropInt("@eventScheduledCount", 0);
- else
- return p->getPropInt("Workflow/@eventScheduledCount", 0); // Legacy location for this setting
- }
- void CLocalWorkUnit::incEventScheduledCount()
- {
- CriticalBlock block(crit);
- p->setPropInt("@eventScheduledCount", queryEventScheduledCount()+1);
- }
- IPropertyTree * CLocalWorkUnit::queryWorkflowTree() const
- {
- CriticalBlock block(crit);
- return p->queryPropTree("Workflow");
- }
- IConstWorkflowItemIterator* CLocalWorkUnit::getWorkflowItems() const
- {
- // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
- CriticalBlock block(crit);
- if(!workflowIteratorCached)
- {
- assertex(!workflowIterator);
- Owned<IPropertyTree> s = p->getPropTree("Workflow");
- if(s)
- workflowIterator.setown(createWorkflowItemIterator(s));
- workflowIteratorCached = true;
- }
- return workflowIterator.getLink();
- }
- IWorkflowItemArray * CLocalWorkUnit::getWorkflowClone() const
- {
- unsigned count = 0;
- Owned<IConstWorkflowItemIterator> iter = getWorkflowItems();
- for(iter->first(); iter->isValid(); iter->next())
- count++;
- Owned<IWorkflowItemArray> array = createWorkflowItemArray(count);
- for(iter->first(); iter->isValid(); iter->next())
- array->addClone(iter->query());
- return array.getLink();
- }
- IWorkflowItem * CLocalWorkUnit::addWorkflowItem(unsigned wfid, WFType type, WFMode mode, unsigned success, unsigned failure, unsigned recovery, unsigned retriesAllowed, unsigned contingencyFor)
- {
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- CriticalBlock block(crit);
- workflowIterator.clear();
- workflowIteratorCached = false;
- IPropertyTree * s = p->queryPropTree("Workflow");
- if(!s)
- s = p->addPropTree("Workflow");
- return createWorkflowItem(s, wfid, type, mode, success, failure, recovery, retriesAllowed, contingencyFor);
- }
- IWorkflowItemIterator * CLocalWorkUnit::updateWorkflowItems()
- {
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- CriticalBlock block(crit);
- if(!workflowIterator)
- {
- IPropertyTree * s = p->queryPropTree("Workflow");
- if(!s)
- s = p->addPropTree("Workflow");
- workflowIterator.setown(createWorkflowItemIterator(s));
- workflowIteratorCached = true;
- }
- return workflowIterator.getLink();
- }
- void CLocalWorkUnit::syncRuntimeWorkflow(IWorkflowItemArray * array)
- {
- Owned<IWorkflowItemIterator> iter = updateWorkflowItems();
- Owned<IWorkflowItem> item;
- for(iter->first(); iter->isValid(); iter->next())
- {
- item.setown(iter->get());
- item->syncRuntimeData(array->queryWfid(item->queryWfid()));
- }
- workflowIterator.clear();
- workflowIteratorCached = false;
- }
- void CLocalWorkUnit::resetWorkflow()
- {
- if (hasWorkflow())
- {
- Owned<IWorkflowItemIterator> iter = updateWorkflowItems();
- Owned<IWorkflowItem> wf;
- for(iter->first(); iter->isValid(); iter->next())
- {
- wf.setown(iter->get());
- wf->reset();
- }
- workflowIterator.clear();
- workflowIteratorCached = false;
- }
- }
- void CLocalWorkUnit::schedule()
- {
- CriticalBlock block(crit);
- if(queryEventScheduledCount() == 0) return;
- switch(getState())
- {
- case WUStateCompleted:
- setState(WUStateWait);
- break;
- case WUStateFailed:
- case WUStateArchived:
- case WUStateAborting:
- case WUStateAborted:
- case WUStateScheduled:
- throw MakeStringException(WUERR_CannotSchedule, "Cannot schedule workunit in this state");
- }
- StringBuffer rootPath;
- rootPath.append("/Schedule/").append(queryClusterName());
- Owned<IRemoteConnection> conn = querySDS().connect(rootPath.str(), myProcessSession(), RTM_LOCK_WRITE | RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- Owned<IPropertyTree> root = conn->getRoot();
- if(!root->hasChildren())
- {
- StringBuffer addPath;
- addPath.append("/Schedulers/").append(queryClusterName());
- Owned<IRemoteConnection> addConn = querySDS().connect(addPath.str(), myProcessSession(), RTM_LOCK_WRITE | RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- }
- char const * wuid = p->queryName();
- StringBuffer xpath("*/*/");
- ncnameEscape(wuid, xpath);
- bool more;
- do more = root->removeProp(xpath.str()); while(more);
- Owned<IConstWorkflowItemIterator> iter = getWorkflowItems();
- Owned<IWorkflowEvent> event;
- Owned<IPropertyTree> branch1, branch2;
- for(iter->first(); iter->isValid(); iter->next())
- {
- event.setown(iter->query()->getScheduleEvent());
- if(!event) continue;
- ncnameEscape(event->queryName(), xpath.clear());
- ensurePTree(root, xpath.str());
- branch1.setown(root->getPropTree(xpath.str()));
- ncnameEscape(event->queryText(), xpath.clear());
- ensurePTree(branch1, xpath.str());
- branch2.setown(branch1->getPropTree(xpath.str()));
- ncnameEscape(wuid, xpath.clear());
- ensurePTree(branch2, xpath.str());
- }
- }
- void CLocalWorkUnit::deschedule()
- {
- if(queryEventScheduledCount() == 0) return;
- if(getState() == WUStateWait)
- setState(WUStateCompleted);
- doDescheduleWorkkunit(p->queryName());
- }
- EnumMapping localFileUploadTypes[] = {
- { UploadTypeFileSpray, "FileSpray" },
- { UploadTypeWUResult, "WUResult" },
- { UploadTypeWUResultCsv, "WUResultCsv" },
- { UploadTypeWUResultXml, "WUResultXml" },
- { UploadTypeSize, NULL }
- };
- class CLocalFileUpload : public CInterface, implements IConstLocalFileUpload
- {
- public:
- CLocalFileUpload(IPropertyTree * _tree) : tree(_tree) {}
- CLocalFileUpload(unsigned id, LocalFileUploadType type, char const * source, char const * destination, char const * eventTag)
- {
- tree.setown(createPTree());
- tree->setPropInt("@id", id);
- setEnum(tree, "@type", type, localFileUploadTypes);
- tree->setProp("@source", source);
- tree->setProp("@destination", destination);
- if (eventTag)
- tree->setProp("@eventTag", eventTag);
- }
- IMPLEMENT_IINTERFACE;
- IPropertyTree * getTree() { return tree.getLink(); }
- virtual unsigned queryID() const { return tree->getPropInt("@id"); }
- virtual LocalFileUploadType queryType() const { return (LocalFileUploadType)getEnum(tree, "@type", localFileUploadTypes); }
- virtual IStringVal & getSource(IStringVal & ret) const { ret.set(tree->queryProp("@source")); return ret; }
- virtual IStringVal & getDestination(IStringVal & ret) const { ret.set(tree->queryProp("@destination")); return ret; }
- virtual IStringVal & getEventTag(IStringVal & ret) const { if(tree->hasProp("@eventTag")) ret.set(tree->queryProp("@eventTag")); else ret.clear(); return ret; }
- private:
- Owned<IPropertyTree> tree;
- };
- class CLocalFileUploadIterator : public CInterface, implements IConstLocalFileUploadIterator
- {
- public:
- CLocalFileUploadIterator(IPropertyTree * _tree) : tree(_tree), iter(tree->getElements("LocalFileUpload")) {}
- IMPLEMENT_IINTERFACE;
- bool first() { return iter->first(); }
- bool isValid() { return iter->isValid(); }
- bool next() { return iter->next(); }
- IConstLocalFileUpload * get() { return new CLocalFileUpload(&iter->get()); }
- private:
- Owned<IPropertyTree> tree;
- Owned<IPropertyTreeIterator> iter;
- };
- IConstLocalFileUploadIterator * CLocalWorkUnit::getLocalFileUploads() const
- {
- // For this to be legally called, we must have the read-able interface. So we are already locked for (at least) read.
- CriticalBlock block(crit);
- Owned<IPropertyTree> s = p->getPropTree("LocalFileUploads");
- if(s)
- return new CLocalFileUploadIterator(s.getClear());
- else
- return NULL;
- }
- bool CLocalWorkUnit::requiresLocalFileUpload() const
- {
- SCMStringBuffer dest;
- Owned<IConstWUResult> result;
- Owned<IConstLocalFileUploadIterator> iter(getLocalFileUploads());
- if(!iter)
- return false;
- for(iter->first(); iter->isValid(); iter->next())
- {
- Owned<IConstLocalFileUpload> upload(iter->get());
- switch(upload->queryType())
- {
- case UploadTypeWUResult:
- case UploadTypeWUResultCsv:
- case UploadTypeWUResultXml:
- upload->getDestination(dest);
- result.setown(getResultByName(dest.str()));
- if(!result)
- return true;
- break;
- default:
- throw MakeStringException(WUERR_InvalidUploadFormat, "Unsupported local file upload type %s", getEnumText(upload->queryType(), localFileUploadTypes));
- }
- }
- return false;
- }
- unsigned CLocalWorkUnit::addLocalFileUpload(LocalFileUploadType type, char const * source, char const * destination, char const * eventTag)
- {
- // For this to be legally called, we must have the write-able interface. So we are already locked for write.
- CriticalBlock block(crit);
- IPropertyTree * s = p->queryPropTree("LocalFileUploads");
- if(!s)
- s = p->addPropTree("LocalFileUploads");
- unsigned id = s->numChildren();
- Owned<CLocalFileUpload> upload = new CLocalFileUpload(id, type, source, destination, eventTag);
- s->addPropTree("LocalFileUpload", upload->getTree());
- return id;
- }
- IStringVal & CLocalWorkUnit::getAbortBy(IStringVal & str) const
- {
- CriticalBlock block(crit);
- str.set(p->queryProp("Tracing/AbortBy"));
- return str;
- }
- unsigned __int64 CLocalWorkUnit::getAbortTimeStamp() const
- {
- CriticalBlock block(crit);
- return p->getPropInt64("Tracing/AbortTimeStamp", 0);
- }
- cost_type CLocalWorkUnit::getExecuteCost() const
- {
- CriticalBlock block(crit);
- return p->getPropInt64("@costExecute");
- }
- cost_type CLocalWorkUnit::getFileAccessCost() const
- {
- CriticalBlock block(crit);
- return p->getPropInt64("@costFileAccess");
- }
- #if 0
- void testConstWorkflow(IConstWorkflowItem * cwf, bool * okay, bool * dep)
- {
- DBGLOG("Test workflow const iface %u", cwf->queryWfid());
- unsigned deps = 0;
- Owned<IWorkflowDependencyIterator> diter;
- switch(cwf->queryWfid())
- {
- case 1:
- assertex(!cwf->isScheduled());
- assertex(cwf->queryType() == WFTypeNormal);
- assertex(cwf->queryState() == WFStateNull);
- diter.setown(cwf->getDependencies());
- for(diter->first(); diter->isValid(); diter->next())
- deps++;
- assertex(deps==0);
- okay[0] = true;
- break;
- case 2:
- assertex(!cwf->isScheduled());
- assertex(cwf->queryType() == WFTypeRecovery);
- assertex(cwf->queryState() == WFStateSkip);
- okay[1] = true;
- break;
- case 3:
- assertex(cwf->queryContingencyFor() == 4);
- okay[2] = true;
- break;
- case 4:
- assertex(cwf->isScheduled());
- assertex(cwf->queryType() == WFTypeNormal);
- assertex(cwf->queryState() == WFStateReqd);
- assertex(cwf->querySuccess() == 0);
- assertex(cwf->queryFailure() == 3);
- assertex(cwf->queryRecovery() == 2);
- assertex(cwf->queryRetriesAllowed() == 10);
- assertex(cwf->queryRetriesRemaining() == 10);
- diter.setown(cwf->getDependencies());
- for(diter->first(); diter->isValid(); diter->next())
- {
- dep[diter->query()-1] = true;
- deps++;
- }
- assertex(deps==2);
- assertex(dep[0]);
- assertex(dep[1]);
- okay[3] = true;
- break;
- case 5:
- assertex(cwf->isScheduled());
- assertex(!cwf->isScheduledNow());
- assertex(cwf->querySchedulePriority() == 75);
- assertex(cwf->queryScheduleCount() == 5);
- assertex(cwf->queryScheduleCountRemaining() == 5);
- okay[4] = true;
- break;
- case 6:
- assertex(cwf->isScheduled());
- assertex(!cwf->isScheduledNow());
- assertex(cwf->querySchedulePriority() == 25);
- assertex(!cwf->hasScheduleCount());
- okay[5] = true;
- break;
- default:
- assertex(!"unknown wfid in test");
- }
- }
- void testRuntimeWorkflow(IRuntimeWorkflowItem * rwf, bool * okay)
- {
- DBGLOG("Test workflow runtime iface %u", rwf->queryWfid());
- switch(rwf->queryWfid())
- {
- case 1:
- case 2:
- case 3:
- okay[rwf->queryWfid()-1] = true;
- break;
- case 4:
- {
- unsigned tries = 0;
- while(rwf->testAndDecRetries())
- tries++;
- assertex(tries == 10);
- assertex(rwf->queryRetriesRemaining() == 0);
- rwf->setState(WFStateFail);
- assertex(rwf->queryState() == WFStateFail);
- rwf->reset();
- assertex(rwf->queryRetriesRemaining() == 10);
- assertex(rwf->queryState() == WFStateReqd);
- }
- okay[3] = true;
- break;
- case 5:
- {
- assertex(rwf->queryScheduleCountRemaining() == 5);
- unsigned count = 0;
- do count++; while(rwf->decAndTestScheduleCountRemaining());
- assertex(count == 5);
- assertex(rwf->queryScheduleCountRemaining() == 0);
- rwf->reset();
- assertex(rwf->queryScheduleCountRemaining() == 5);
- }
- okay[4] = true;
- break;
- case 6:
- {
- assertex(!rwf->hasScheduleCount());
- unsigned count;
- for(count=0; count<20; count++)
- assertex(rwf->decAndTestScheduleCountRemaining());
- }
- okay[5] = true;
- break;
- default:
- assertex(!"unknown wfid in test");
- }
- }
- void testWorkflow()
- {
- DBGLOG("workunit.cpp : testWorkflow");
- CLocalWorkUnit wu("W-WF-TEST", 0, 0, 0);
- Owned<IWorkflowItem> wf;
- wf.setown(wu.addWorkflowItem(1, WFTypeNormal, 0, 0, 0, 0, 0));
- wf.setown(wu.addWorkflowItem(2, WFTypeRecovery, 0, 0, 0, 0, 0));
- wf.setown(wu.addWorkflowItem(3, WFTypeFailure, 0, 0, 0, 0, 4));
- wf.setown(wu.addWorkflowItem(4, WFTypeNormal, 0, 3, 2, 10, 0));
- wf->setScheduledNow();
- wf->addDependency(1);
- wf.setown(wu.addWorkflowItem(5, WFTypeNormal, 0, 0, 0, 0, 0));
- wf->setScheduledOn("test", "foo*");
- wf->setSchedulePriority(75);
- wf->setScheduleCount(5);
- wf.setown(wu.addWorkflowItem(6, WFTypeNormal, 0, 0, 0, 0, 0));
- wf->setScheduledOn("test", "bar*");
- wf->setSchedulePriority(25);
- unsigned const n = 6;
- bool okay[n];
- bool dep[n];
- unsigned i;
- for(i=0; i<n; i++)
- okay[i] = dep[i] = 0;
- Owned<IConstWorkflowItemIterator> citer(wu.getWorkflowItems());
- for(citer->first(); citer->isValid(); citer->next())
- testConstWorkflow(citer->query(), okay, dep);
- for(i=0; i<n; i++)
- {
- assertex(okay[i]);
- okay[i] = false;
- }
- Owned<IWorkflowItemIterator> miter(wu.updateWorkflowItems());
- for(miter->first(); miter->isValid(); miter->next())
- {
- Owned<IRuntimeWorkflowItem> rwf(miter->get());
- testRuntimeWorkflow(rwf, okay);
- }
- for(i=0; i<n; i++)
- {
- assertex(okay[i]);
- okay[i] = dep[i] = false;
- }
- Owned<IWorkflowItemArray> array(wu.getWorkflowClone());
- unsigned wfid;
- for(wfid = 1; array->isValid(wfid); wfid++)
- testConstWorkflow(&array->queryWfid(wfid), okay, dep);
- for(i=0; i<n; i++)
- {
- assertex(okay[i]);
- okay[i] = false;
- }
- for(wfid = 1; array->isValid(wfid); wfid++)
- testRuntimeWorkflow(&array->queryWfid(wfid), okay);
- for(i=0; i<n; i++)
- {
- assertex(okay[i]);
- okay[i] = false;
- }
- }
- #endif
- //------------------------------------------------------------------------------------------
- extern WUState waitForWorkUnitToComplete(const char * wuid, int timeout, std::list<WUState> expectedStates)
- {
- return globalFactory->waitForWorkUnit(wuid, (unsigned) timeout, false, expectedStates);
- }
- extern WORKUNIT_API WUState secWaitForWorkUnitToComplete(const char * wuid, ISecManager *secmgr, ISecUser *secuser, int timeout, std::list<WUState> expectedStates)
- {
- if (checkWuSecAccess(wuid, secmgr, secuser, SecAccess_Read, "Wait for Complete", false, true))
- return waitForWorkUnitToComplete(wuid, timeout, expectedStates);
- return WUStateUnknown;
- }
- extern bool waitForWorkUnitToCompile(const char * wuid, int timeout)
- {
- switch(globalFactory->waitForWorkUnit(wuid, (unsigned) timeout, true, { WUStateWait }))
- {
- case WUStateCompiled:
- case WUStateCompleted:
- case WUStateWait:
- case WUStateUploadingFiles:
- return true;
- default:
- return false;
- }
- }
- extern WORKUNIT_API bool secWaitForWorkUnitToCompile(const char * wuid, ISecManager &secmgr, ISecUser &secuser, int timeout)
- {
- if (checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Read, "Wait for Compile", false, true))
- return waitForWorkUnitToCompile(wuid, timeout);
- return false;
- }
- extern WORKUNIT_API bool secDebugWorkunit(const char * wuid, ISecManager &secmgr, ISecUser &secuser, const char *command, StringBuffer &response)
- {
- if (strnicmp(command, "<debug:", 7) == 0 && checkWuSecAccess(wuid, &secmgr, &secuser, SecAccess_Read, "Debug", false, true))
- {
- Owned<IConstWorkUnit> wu = globalFactory->openWorkUnit(wuid, &secmgr, &secuser);
- SCMStringBuffer ip;
- unsigned port = 0;
- try
- {
- port = wu->getDebugAgentListenerPort();
- wu->getDebugAgentListenerIP(ip);
- SocketEndpoint debugEP(ip.str(), port);
- Owned<ISocket> socket = ISocket::connect_timeout(debugEP, 1000);
- unsigned len = (size32_t)strlen(command);
- unsigned revlen = len;
- _WINREV(revlen);
- socket->write(&revlen, sizeof(revlen));
- socket->write(command, len);
- for (;;)
- {
- socket->read(&len, sizeof(len));
- _WINREV(len);
- if (len == 0)
- break;
- if (len & 0x80000000)
- {
- throwUnexpected();
- }
- char * mem = (char*) response.reserve(len);
- socket->read(mem, len);
- }
- return true;
- }
- catch (IException *E)
- {
- VStringBuffer msg("In secDebugWorkunit wuid %s port %d ip %s command %s", wuid, port, ip.str(), command);
- EXCLOG(E, msg.str());
- throw;
- }
- }
- return false;
- }
- void getSimpleResultType(IWUResult *r, Owned<ITypeInfo> &type)
- {
- TypeInfoArray types;
- StringAttrArray names;
- r->getSchema(types, names, NULL);
- if (types.ordinality()==1)
- type.set(&types.item(0));
- }
- bool isSuppliedParamScalar(IWUResult *r, IPropertyTree &curVal, Owned<ITypeInfo> &type)
- {
- if (!r->isResultScalar())
- return false;
- if (!curVal.hasChildren())
- return true;
- getSimpleResultType(r, type);
- return type && type->isScalar();
- }
- void updateSuppliedXmlParams(IWorkUnit * w)
- {
- Owned<const IPropertyTree> params = w->getXmlParams();
- if (!params)
- return;
- Owned<IPropertyTreeIterator> elems = params->getElements("*");
- ForEach(*elems)
- {
- IPropertyTree & curVal = elems->query();
- const char *name = curVal.queryName();
- Owned<IWUResult> r = updateWorkUnitResult(w, name, -1);
- if (r)
- {
- Owned<ITypeInfo> type;
- StringBuffer s;
- if (isSuppliedParamScalar(r, curVal, type))
- {
- curVal.getProp(".", s);
- r->setResultXML(s);
- r->setResultStatus(ResultStatusSupplied);
- }
- else
- {
- toXML(&curVal, s);
- if (!type)
- getSimpleResultType(r, type);
- bool isSet = (type && type->getTypeCode()==type_set);
- r->setResultRaw(s.length(), s.str(), isSet ? ResultFormatXmlSet : ResultFormatXml);
- }
- }
- else
- DBGLOG("WARNING: no matching variable in workunit for input parameter %s", name);
- }
- }
- IWUResult * updateWorkUnitResult(IWorkUnit * w, const char *name, unsigned sequence)
- {
- switch ((int)sequence)
- {
- case ResultSequenceStored:
- return w->updateVariableByName(name);
- case ResultSequencePersist:
- return w->updateGlobalByName(name);
- case ResultSequenceInternal:
- case ResultSequenceOnce:
- return w->updateTemporaryByName(name);
- default:
- return w->updateResultBySequence(sequence);
- }
- }
- IConstWUResult * getWorkUnitResult(IConstWorkUnit * w, const char *name, unsigned sequence)
- {
- switch ((int)sequence)
- {
- case ResultSequenceStored:
- return w->getVariableByName(name);
- case ResultSequencePersist:
- return w->getGlobalByName(name);
- case ResultSequenceInternal:
- case ResultSequenceOnce:
- return w->getTemporaryByName(name);
- default:
- if (name && name[0])
- return w->getResultByName(name);//name takes precedence over sequence
- else
- return w->getResultBySequence(sequence);
- }
- }
- extern WORKUNIT_API bool getWorkUnitCreateTime(const char *wuid,CDateTime &time)
- {
- if (wuid) {
- char prefchar;
- unsigned year,month,day,hour,min,sec;
- if (sscanf(wuid, "%c%4u%2u%2u-%2u%2u%2u", &prefchar, &year, &month, &day, &hour, &min, &sec)==7) {
- time.set(year, month, day, hour, min, sec, 0, true);
- // time.setDate(year, month, day);
- // time.setTime(hour, min, sec, 0, true); // for some reason time is local
- return true;
- }
- }
- return false;
- }
- extern WORKUNIT_API WUState getWorkUnitState(const char* state)
- {
- return (WUState) getEnum(state, states);
- }
- constexpr LogMsgCategory MCschedconn = MCprogress(1000); // Category used to inform about schedule synchronization
- class CWorkflowScheduleConnection : implements IWorkflowScheduleConnection, public CInterface
- {
- public:
- CWorkflowScheduleConnection(char const * wuid)
- {
- basexpath.append("/WorkflowSchedule/").append(wuid);
- flagxpath.append(basexpath.str()).append("/Active");
- }
- IMPLEMENT_IINTERFACE;
- virtual void lock()
- {
- LOG(MCschedconn, "Locking base schedule connection");
- baseconn.setown(querySDS().connect(basexpath.str(), myProcessSession(), RTM_CREATE_QUERY | RTM_LOCK_WRITE, INFINITE));
- if(!baseconn)
- throw MakeStringException(WUERR_ScheduleLockFailed, "Could not get base workflow schedule lock");
- }
- virtual void unlock()
- {
- LOG(MCschedconn, "Unlocking base schedule connection");
- baseconn.clear();
- }
- virtual void setActive()
- {
- LOG(MCschedconn, "Setting active flag in schedule connection");
- flagconn.setown(querySDS().connect(flagxpath.str(), myProcessSession(), RTM_CREATE | RTM_LOCK_WRITE | RTM_DELETE_ON_DISCONNECT, INFINITE));
- if(!flagconn)
- throw MakeStringException(WUERR_ScheduleLockFailed, "Could not get active workflow schedule lock");
- }
- virtual void resetActive()
- {
- LOG(MCschedconn, "Resetting active flag in schedule connection");
- flagconn.clear();
- }
- virtual bool queryActive()
- {
- return baseconn->queryRoot()->hasProp("Active");
- }
- virtual bool pull(IWorkflowItemArray * workflow)
- {
- assertex(baseconn);
- Owned<IPropertyTree> root = baseconn->getRoot();
- Owned<IPropertyTree> eventQueue = root->getPropTree("EventQueue");
- if(!eventQueue) return false;
- if(!eventQueue->hasProp("Item")) return false;
- {
- Owned<IPropertyTreeIterator> eventItems = eventQueue->getElements("Item");
- Owned<IPropertyTree> eventItem;
- Owned<IRuntimeWorkflowItemIterator> wfItems = workflow->getSequenceIterator();
- Owned<IRuntimeWorkflowItem> wfItem;
- for(eventItems->first(); eventItems->isValid(); eventItems->next())
- {
- eventItem.setown(&eventItems->get());
- const char * eventName = eventItem->queryProp("@name");
- const char * eventText = eventItem->queryProp("@text");
- for(wfItems->first(); wfItems->isValid(); wfItems->next())
- {
- wfItem.setown(wfItems->get());
- if(wfItem->queryState() != WFStateWait)
- continue;
- Owned<IWorkflowEvent> targetEvent = wfItem->getScheduleEvent();
- if(!targetEvent || !targetEvent->matches(eventName, eventText))
- continue;
- wfItem->setEvent(eventName, eventText);
- wfItem->setState(WFStateReqd);
- resetDependentsState(workflow, *wfItem);
- }
- }
- }
- bool more;
- do
- more = eventQueue->removeProp("Item");
- while(more);
- return true;
- }
- virtual void push(char const * name, char const * text)
- {
- assertex(baseconn);
- Owned<IPropertyTree> root = baseconn->getRoot();
- IPropertyTree *eventQueue = ensurePTree(root, "EventQueue");
- IPropertyTree *eventItem = eventQueue->addPropTree("Item");
- eventItem->setProp("@name", name);
- eventItem->setProp("@text", text);
- }
- virtual void remove()
- {
- if (baseconn)
- {
- baseconn->close(true);
- baseconn.clear();
- }
- }
- private:
- void resetItemStateAndDependents(IWorkflowItemArray * workflow, unsigned wfid) const
- {
- if (wfid)
- resetItemStateAndDependents(workflow, workflow->queryWfid(wfid));
- }
- void resetItemStateAndDependents(IWorkflowItemArray * workflow, IRuntimeWorkflowItem & item) const
- {
- switch(item.queryState())
- {
- case WFStateDone:
- case WFStateFail:
- {
- item.setState(WFStateNull);
- resetItemStateAndDependents(workflow, item.queryPersistWfid());
- resetDependentsState(workflow, item);
- break;
- }
- }
- }
- void resetDependentsState(IWorkflowItemArray * workflow, IRuntimeWorkflowItem & item) const
- {
- Owned<IWorkflowDependencyIterator> iter(item.getDependencies());
- for(iter->first(); iter->isValid(); iter->next())
- {
- IRuntimeWorkflowItem & dep = workflow->queryWfid(iter->query());
- resetItemStateAndDependents(workflow, dep);
- }
- }
- private:
- StringBuffer basexpath;
- StringBuffer flagxpath;
- Owned<IRemoteConnection> baseconn;
- Owned<IRemoteConnection> flagconn;
- };
- extern WORKUNIT_API IWorkflowScheduleConnection * getWorkflowScheduleConnection(char const * wuid)
- {
- return new CWorkflowScheduleConnection(wuid);
- }
- extern WORKUNIT_API IExtendedWUInterface * queryExtendedWU(IConstWorkUnit * wu)
- {
- return QUERYINTERFACE(wu, IExtendedWUInterface);
- }
- extern WORKUNIT_API const IExtendedWUInterface * queryExtendedWU(const IConstWorkUnit * wu)
- {
- return QUERYINTERFACE(wu, const IExtendedWUInterface);
- }
- 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)
- {
- Owned<IWUException> we = wu->createException();
- we->setSeverity(severity);
- we->setExceptionMessage(text);
- if (source)
- we->setExceptionSource(source);
- if (code)
- we->setExceptionCode(code);
- if (filename)
- we->setExceptionFileName(filename);
- if (lineno)
- {
- we->setExceptionLineNo(lineno);
- if (column)
- we->setExceptionColumn(lineno);
- }
- if (activity)
- we->setActivityId(activity);
- }
- const char * skipLeadingXml(const char * text)
- {
- if (!text)
- return NULL;
- //skip utf8 BOM, probably excessive
- if (memcmp(text, UTF8_BOM, 3) == 0)
- text += 3;
- for (;;)
- {
- if (isspace(*text))
- text++;
- else if (text[0] == '<' && text[1] == '?')
- {
- text += 2;
- for (;;)
- {
- if (!*text) break;
- if (text[0] == '?' && text[1] == '>')
- {
- text += 2;
- break;
- }
- text++;
- }
- }
- else if (text[0] == '<' && text[1] == '!' && text[2] == '-' && text[3] == '-')
- {
- text += 4;
- for (;;)
- {
- if (!*text) break;
- if (text[0] == '-' && text[1] == '-' && text[2] == '>')
- {
- text += 3;
- break;
- }
- text++;
- }
- }
- else
- break;
- }
- return text;
- }
- extern WORKUNIT_API bool isArchiveQuery(const char * text)
- {
- text = skipLeadingXml(text);
- if (!text)
- return false;
- const char * archivePrefix = "<Archive";
- return memicmp(text, archivePrefix, strlen(archivePrefix)) == 0;
- }
- extern WORKUNIT_API bool isQueryManifest(const char * text)
- {
- text = skipLeadingXml(text);
- if (!text)
- return false;
- const char * manifestPrefix = "<Manifest";
- return memicmp(text, manifestPrefix, strlen(manifestPrefix)) == 0;
- }
- //------------------------------------------------------------------------------
- // Named Alias helper function
- static IPropertyTree * resolveQueryByDll(IPropertyTree * queryRegistry, const char * dll)
- {
- StringBuffer xpath;
- xpath.append("Query[@dll=\"").append(dll).append("\"]");
- return queryRegistry->getPropTree(xpath);
- }
- static IPropertyTree * resolveQueryByWuid(IPropertyTree * queryRegistry, const char * wuid)
- {
- StringBuffer xpath;
- xpath.append("Query[@wuid=\"").append(wuid).append("\"]");
- return queryRegistry->getPropTree(xpath);
- }
- static void clearAliases(IPropertyTree * queryRegistry, const char * id)
- {
- StringBuffer lcId(id);
- lcId.toLowerCase();
- StringBuffer xpath;
- xpath.append("Alias[@id=\"").append(lcId).append("\"]");
- while (queryRegistry->removeProp(xpath));
- }
- IPropertyTree * addNamedQuery(IPropertyTree * queryRegistry, const char * name, const char * wuid, const char * dll, bool library, const char *userid, const char *snapshot)
- {
- StringBuffer lcName(name);
- lcName.toLowerCase();
- StringBuffer xpath;
- xpath.append("Query[@name=\"").append(lcName.str()).append("\"]");
- Owned<IPropertyTreeIterator> iter = queryRegistry->getElements(xpath);
- unsigned seq = 1;
- ForEach(*iter)
- {
- IPropertyTree &item = iter->query();
- const char *thisWuid = item.queryProp("@wuid");
- if (strieq(wuid, thisWuid))
- return &item;
- unsigned thisSeq = item.getPropInt("@seq");
- if (thisSeq >= seq)
- seq = thisSeq + 1;
- }
- StringBuffer id;
- id.append(lcName).append(".").append(seq);
- IPropertyTree * newEntry = createPTree("Query", ipt_caseInsensitive|ipt_lowmem);
- newEntry->setProp("@name", lcName);
- newEntry->setProp("@wuid", wuid);
- newEntry->setProp("@dll", dll);
- newEntry->setProp("@id", id);
- newEntry->setPropInt("@seq", seq);
- if (library)
- newEntry->setPropBool("@isLibrary", true);
- if (userid && *userid)
- newEntry->setProp("@publishedBy", userid);
- if (snapshot && *snapshot)
- newEntry->setProp("@snapshot", snapshot);
- return queryRegistry->addPropTree("Query", newEntry);
- }
- void removeNamedQuery(IPropertyTree * queryRegistry, const char * id)
- {
- StringBuffer lcId(id);
- lcId.toLowerCase();
- clearAliases(queryRegistry, lcId);
- StringBuffer xpath;
- xpath.append("Query[@id=\"").append(lcId).append("\"]");
- queryRegistry->removeProp(xpath);
- }
- void removeDllFromNamedQueries(IPropertyTree * queryRegistry, const char * dll)
- {
- Owned<IPropertyTree> match = resolveQueryByDll(queryRegistry, dll);
- if (!match)
- return;
- clearAliases(queryRegistry, match->queryProp("@id"));
- queryRegistry->removeTree(match);
- }
- void removeWuidFromNamedQueries(IPropertyTree * queryRegistry, const char * wuid)
- {
- Owned<IPropertyTree> match = resolveQueryByWuid(queryRegistry, wuid);
- if (!match)
- return;
- clearAliases(queryRegistry, match->queryProp("@id"));
- queryRegistry->removeTree(match);
- }
- void removeAliasesFromNamedQuery(IPropertyTree * queryRegistry, const char * id)
- {
- clearAliases(queryRegistry, id);
- }
- void setQueryAlias(IPropertyTree * queryRegistry, const char * name, const char * value)
- {
- StringBuffer lcName(name);
- lcName.toLowerCase();
- StringBuffer xpath;
- xpath.append("Alias[@name=\"").append(lcName).append("\"]");
- IPropertyTree * match = queryRegistry->queryPropTree(xpath);
- if (!match)
- {
- match = queryRegistry->addPropTree("Alias");
- match->setProp("@name", lcName);
- }
- match->setProp("@id", value);
- }
- extern WORKUNIT_API IPropertyTree * getQueryById(IPropertyTree * queryRegistry, const char *queryid)
- {
- if (!queryRegistry || !queryid)
- return NULL;
- StringBuffer xpath;
- xpath.append("Query[@id=\"").append(queryid).append("\"]");
- return queryRegistry->getPropTree(xpath);
- }
- extern WORKUNIT_API IPropertyTree * getQueryById(const char *queryset, const char *queryid, bool readonly)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(queryset, readonly);
- return getQueryById(queryRegistry, queryid);
- }
- extern WORKUNIT_API IPropertyTree * resolveQueryAlias(IPropertyTree * queryRegistry, const char * alias)
- {
- if (!queryRegistry || !alias)
- return NULL;
- StringBuffer xpath;
- unsigned cnt = 0;
- StringBuffer lc(alias);
- const char * search = lc.toLowerCase().str();
- for (;;)
- {
- xpath.set("Alias[@name='").append(search).append("']/@id");
- const char * queryId = queryRegistry->queryProp(xpath);
- if (!queryId)
- break;
- //Check for too many alias indirections.
- if (cnt++ > 10)
- return NULL;
- search = lc.set(queryId).toLowerCase().str();
- }
- return getQueryById(queryRegistry, search);
- }
- extern WORKUNIT_API IPropertyTree * resolveQueryAlias(const char *queryset, const char *alias, bool readonly)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(queryset, readonly);
- return resolveQueryAlias(queryRegistry, alias);
- }
- void setQuerySuspendedState(IPropertyTree * queryRegistry, const char *id, bool suspend, const char *userid)
- {
- StringBuffer lcId(id);
- lcId.toLowerCase();
- StringBuffer xpath;
- xpath.append("Query[@id=\"").append(lcId).append("\"]");
- IPropertyTree *tree = queryRegistry->queryPropTree(xpath);
- if (tree)
- {
- if (tree->getPropBool("@suspended", false) == suspend)
- return;
- if (suspend)
- {
- tree->addPropBool("@suspended", true);
- if (userid && *userid)
- tree->addProp("@suspendedBy", userid);
- }
- else
- {
- tree->removeProp("@suspended");
- tree->removeProp("@suspendedBy");
- }
- }
- else
- throw MakeStringException((suspend)? QUERRREG_SUSPEND : QUERRREG_UNSUSPEND, "Modifying query suspended state failed. Could not find query %s", id);
- }
- void setQueryCommentForNamedQuery(IPropertyTree * queryRegistry, const char *id, const char *queryComment)
- {
- if (queryComment)
- {
- StringBuffer lcId(id);
- lcId.toLowerCase();
- StringBuffer xpath;
- xpath.append("Query[@id=\"").append(lcId).append("\"]");
- IPropertyTree *tree = queryRegistry->queryPropTree(xpath);
- if (tree)
- tree->setProp("@queryComment", queryComment);
- else
- throw MakeStringException(QUERRREG_COMMENT, "Could not find query %s", id);
- }
- }
- extern WORKUNIT_API IPropertyTree * getQueryRegistryRoot()
- {
- Owned<IRemoteConnection> conn = querySDS().connect("/QuerySets", myProcessSession(), RTM_LOCK_READ, SDS_LOCK_TIMEOUT);
- if (conn)
- return conn->getRoot();
- else
- return NULL;
- }
- extern WORKUNIT_API void checkAddLibrariesToQueryEntry(IPropertyTree *queryTree, IConstWULibraryIterator *libraries)
- {
- if (!queryTree || !libraries)
- return;
- if (queryTree->hasProp("@libCount")) //already added
- return;
- unsigned libCount=0;
- ForEach(*libraries)
- {
- IConstWULibrary &library = libraries->query();
- SCMStringBuffer libname;
- if (!library.getName(libname).length())
- continue;
- queryTree->addProp("Library", libname.str());
- libCount++;
- }
- queryTree->setPropInt("@libCount", libCount);
- }
- extern WORKUNIT_API void checkAddLibrariesToQueryEntry(IPropertyTree *queryTree, IConstWorkUnit *cw)
- {
- Owned<IConstWULibraryIterator> libraries = &cw->getLibraries();
- checkAddLibrariesToQueryEntry(queryTree, libraries);
- }
- extern WORKUNIT_API IPropertyTree * getQueryRegistry(const char * wsEclId, bool readonly)
- {
- //Only lock the branch for the target we're interested in.
- StringBuffer xpath;
- xpath.append("/QuerySets/QuerySet[@id=\"").append(wsEclId).append("\"]");
- Owned<IRemoteConnection> conn = querySDS().connect(xpath.str(), myProcessSession(), readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if (conn)
- return conn->getRoot();
- if (readonly)
- return NULL;
- //Lock the QuerySets in case another thread/client wants to check/add the same QuerySet.
- Owned<IRemoteConnection> globalLock = querySDS().connect("/QuerySets/", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- //Re-check if the QuerySet has been added between checking the 1st time and gaining the globalLock.
- conn.setown(querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
- if (conn)
- return conn->getRoot();
- conn.setown(querySDS().connect("/QuerySets/QuerySet", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_ADD, SDS_LOCK_TIMEOUT));
- if (!conn)
- throwUnexpected();
- IPropertyTree * root = conn->queryRoot();
- root->setProp("@id",wsEclId);
- conn->commit();
- return LINK(root);
- }
- IPropertyTree * addNamedPackageSet(IPropertyTree * packageRegistry, const char * name, IPropertyTree *packageInfo, bool overWrite)
- {
- StringBuffer xpath;
- StringBuffer lcName(name);
- lcName.toLowerCase();
- // see if "name" already exists
- xpath.append("Package[@id='").append(name).append("']");
- IPropertyTree *pkgTree = packageRegistry->queryPropTree(xpath.str());
- if (pkgTree)
- {
- if (overWrite)
- packageRegistry->removeTree(pkgTree);
- else
- throw MakeStringException(WUERR_PackageAlreadyExists, "Package name %s already exists, either delete it or specify overwrite",name);
- }
- IPropertyTree *tree = packageRegistry->addPropTree("Package", packageInfo);
- tree->setProp("@id", lcName);
- return tree;
- }
- void removeNamedPackage(IPropertyTree * packageRegistry, const char * id)
- {
- StringBuffer lcId(id);
- lcId.toLowerCase();
- StringBuffer xpath;
- xpath.append("Package[@id=\"").append(lcId).append("\"]");
- packageRegistry->removeProp(xpath);
- }
- extern WORKUNIT_API IPropertyTree * getPackageSetRegistry(const char * wsEclId, bool readonly)
- {
- //Only lock the branch for the target we're interested in.
- StringBuffer xpath;
- xpath.append("/PackageSets/PackageSet[@id=\"").append(wsEclId).append("\"]");
- Owned<IRemoteConnection> conn = querySDS().connect(xpath.str(), myProcessSession(), readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if (conn)
- return conn->getRoot();
- if (readonly)
- return NULL;
- //Lock the PackageSets in case another thread/client wants to check/add the same PackageSet.
- Owned<IRemoteConnection> globalLock = querySDS().connect("/PackageSets/", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, SDS_LOCK_TIMEOUT);
- //Re-check if the PackageSet has been added between checking the 1st time and gaining the globalLock.
- conn.setown(querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT));
- if (conn)
- return conn->getRoot();
- conn.setown(querySDS().connect("/PackageSets/PackageSet", myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_ADD, SDS_LOCK_TIMEOUT));
- if (!conn)
- throwUnexpected();
- IPropertyTree* root = conn->queryRoot();
- root->setProp("@id",wsEclId);
- conn->commit();
- return LINK(root);
- }
- void addQueryToQuerySet(IWorkUnit *workunit, IPropertyTree *queryRegistry, const char *queryName, WUQueryActivationOptions activateOption, StringBuffer &newQueryId, const char *userid)
- {
- StringBuffer cleanQueryName;
- appendUtf8XmlName(cleanQueryName, strlen(queryName), queryName);
- SCMStringBuffer dllName;
- Owned<IConstWUQuery> q = workunit->getQuery();
- q->getQueryDllName(dllName);
- if (!dllName.length())
- throw MakeStringException(WUERR_InvalidDll, "Cannot deploy query - no associated dll.");
- StringBuffer currentTargetClusterType;
- queryRegistry->getProp("@targetclustertype", currentTargetClusterType);
- SCMStringBuffer targetClusterType;
- workunit->getDebugValue("targetclustertype", targetClusterType);
- SCMStringBuffer snapshot;
- workunit->getSnapshot(snapshot);
- if (currentTargetClusterType.length() < 1)
- {
- queryRegistry->setProp("@targetclustertype", targetClusterType.str());
- }
- else
- {
- if (strcmp(currentTargetClusterType.str(), "roxie") == 0 && strcmp(currentTargetClusterType.str(), targetClusterType.str())!=0)
- {
- throw MakeStringException(WUERR_MismatchClusterType, "TargetClusterTypes of workunit and queryset do not match.");
- }
- }
- IPropertyTree *newEntry = addNamedQuery(queryRegistry, cleanQueryName, workunit->queryWuid(), dllName.str(), isLibrary(workunit), userid, snapshot.str());
- Owned<IConstWULibraryIterator> libraries = &workunit->getLibraries();
- checkAddLibrariesToQueryEntry(newEntry, libraries);
- newQueryId.append(newEntry->queryProp("@id"));
- workunit->setIsQueryService(true); //will check querysets before delete
- workunit->commit();
- activateQuery(queryRegistry, activateOption, queryName, newQueryId, userid);
- }
- void activateQuery(IPropertyTree *queryRegistry, WUQueryActivationOptions activateOption, const char *queryName, const char *queryId, const char *userid)
- {
- StringBuffer cleanQueryName;
- appendUtf8XmlName(cleanQueryName, strlen(queryName), queryName);
- if (activateOption == ACTIVATE_SUSPEND_PREVIOUS|| activateOption == ACTIVATE_DELETE_PREVIOUS)
- {
- Owned<IPropertyTree> prevQuery = resolveQueryAlias(queryRegistry, cleanQueryName);
- setQueryAlias(queryRegistry, cleanQueryName, queryId);
- if (prevQuery && !streq(queryId, prevQuery->queryProp("@id")))
- {
- if (activateOption == ACTIVATE_SUSPEND_PREVIOUS)
- setQuerySuspendedState(queryRegistry, prevQuery->queryProp("@id"), true, userid);
- else
- removeNamedQuery(queryRegistry, prevQuery->queryProp("@id"));
- }
- }
- else if (activateOption == MAKE_ACTIVATE || activateOption == MAKE_ACTIVATE_LOAD_DATA_ONLY)
- setQueryAlias(queryRegistry, cleanQueryName, queryId);
- }
- void addQueryToQuerySet(IWorkUnit *workunit, const char *querySetName, const char *queryName, WUQueryActivationOptions activateOption, StringBuffer &newQueryId, const char *userid)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, false);
- addQueryToQuerySet(workunit, queryRegistry, queryName, activateOption, newQueryId, userid);
- }
- bool removeQuerySetAlias(const char *querySetName, const char *alias)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
- StringBuffer xpath;
- xpath.appendf("Alias[@name='%s']", alias);
- IPropertyTree *t = queryRegistry->queryPropTree(xpath);
- return queryRegistry->removeTree(t);
- }
- void addQuerySetAlias(const char *querySetName, const char *alias, const char *id)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, false);
- setQueryAlias(queryRegistry, alias, id);
- }
- void setSuspendQuerySetQuery(const char *querySetName, const char *id, bool suspend, const char *userid)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
- setQuerySuspendedState(queryRegistry, id, suspend, userid);
- }
- void deleteQuerySetQuery(const char *querySetName, const char *id)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
- removeNamedQuery(queryRegistry, id);
- }
- void removeQuerySetAliasesFromNamedQuery(const char *querySetName, const char * id)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
- clearAliases(queryRegistry, id);
- }
- void setQueryCommentForNamedQuery(const char *querySetName, const char *id, const char *queryComment)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
- setQueryCommentForNamedQuery(queryRegistry, id, queryComment);
- }
- const char *queryIdFromQuerySetWuid(IPropertyTree *queryRegistry, const char *wuid, const char *queryName, IStringVal &id)
- {
- if (!queryRegistry)
- return NULL;
- StringBuffer xpath;
- xpath.appendf("Query[@wuid='%s']", wuid);
- if (queryName && *queryName)
- xpath.appendf("[@name='%s']", queryName);
- IPropertyTree *q = queryRegistry->queryPropTree(xpath.str());
- if (q)
- {
- id.set(q->queryProp("@id"));
- }
- return id.str();
- }
- const char *queryIdFromQuerySetWuid(const char *querySetName, const char *wuid, const char *queryName, IStringVal &id)
- {
- Owned<IPropertyTree> queryRegistry = getQueryRegistry(querySetName, true);
- return queryIdFromQuerySetWuid(queryRegistry, wuid, queryName, id);
- }
- extern WORKUNIT_API void gatherLibraryNames(StringArray &names, StringArray &unresolved, IWorkUnitFactory &workunitFactory, IConstWorkUnit &cw, IPropertyTree *queryset)
- {
- Owned<IConstWULibraryIterator> wulibraries = &cw.getLibraries();
- ForEach(*wulibraries)
- {
- SCMStringBuffer libname;
- IConstWULibrary &wulibrary = wulibraries->query();
- wulibrary.getName(libname);
- if (names.contains(libname.str()) || unresolved.contains(libname.str()))
- continue;
- Owned<IPropertyTree> query = resolveQueryAlias(queryset, libname.str());
- if (query && query->getPropBool("@isLibrary"))
- {
- const char *wuid = query->queryProp("@wuid");
- Owned<IConstWorkUnit> libcw = workunitFactory.openWorkUnit(wuid);
- if (libcw)
- {
- names.appendUniq(libname.str());
- gatherLibraryNames(names, unresolved, workunitFactory, *libcw, queryset);
- continue;
- }
- }
- unresolved.appendUniq(libname.str());
- }
- }
- bool looksLikeAWuid(const char * wuid, const char firstChar)
- {
- if (!wuid)
- return false;
- if (wuid[0] != firstChar)
- return false;
- if (!isdigit(wuid[1]) || !isdigit(wuid[2]) || !isdigit(wuid[3]) || !isdigit(wuid[4]))
- return false;
- if (!isdigit(wuid[5]) || !isdigit(wuid[6]) || !isdigit(wuid[7]) || !isdigit(wuid[8]))
- return false;
- return (wuid[9]=='-');
- }
- IPropertyTree * resolveDefinitionInArchive(IPropertyTree * archive, const char * path)
- {
- IPropertyTree * module = archive;
- const char * dot = strrchr(path, '.');
- StringBuffer xpath;
- if (dot)
- {
- xpath.clear().append("Module[@key='").appendLower(dot-path, path).append("']");
- module = archive->queryPropTree(xpath);
- path = dot+1;
- }
- else
- module = archive->queryPropTree("Module[@key='']");
- if (!module)
- return NULL;
- xpath.clear().append("Attribute[@key='").appendLower(strlen(path), path).append("']");
- return module->queryPropTree(xpath);
- }
- extern WORKUNIT_API void associateLocalFile(IWUQuery * query, WUFileType type, const char * name, const char * description, unsigned crc, unsigned minActivity, unsigned maxActivity)
- {
- StringBuffer fullPathName;
- makeAbsolutePath(name, fullPathName);
- if (isContainerized())
- {
- StringBuffer dllDirectory;
- if (getConfigurationDirectory(nullptr, "query", nullptr, nullptr, dllDirectory))
- {
- StringBuffer destPathName(dllDirectory);
- addNonEmptyPathSepChar(destPathName);
- splitFilename(fullPathName.str(), nullptr, nullptr, &destPathName, &destPathName);
- OwnedIFile source = createIFile(fullPathName);
- OwnedIFile target = createIFile(destPathName);
- if (!target->exists())
- {
- source->copyTo(target, 0, NULL, true);
- }
- query->addAssociatedFile(type, destPathName, "localhost", description, crc, minActivity, maxActivity);
- // Should we delete the local files? No - they may not be finished with
- }
- else
- {
- //Containerized stand alone eclcc or other examples with no configuration file
- query->addAssociatedFile(type, fullPathName, "localhost", description, crc, minActivity, maxActivity);
- }
- }
- else
- {
- StringBuffer hostname;
- queryHostIP().getIpText(hostname);
- query->addAssociatedFile(type, fullPathName, hostname, description, crc, minActivity, maxActivity);
- }
- }
- extern WORKUNIT_API void descheduleWorkunit(char const * wuid)
- {
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- Owned<IWorkUnit> workunit = factory->updateWorkUnit(wuid);
- if(workunit)
- workunit->deschedule();
- else
- doDescheduleWorkkunit(wuid);
- }
- extern WORKUNIT_API void updateWorkunitStat(IWorkUnit * wu, StatisticScopeType scopeType, const char * scope, StatisticKind kind, const char * description, unsigned __int64 value, unsigned wfid)
- {
- StringBuffer scopestr;
- if (wfid && scope && *scope)
- scopestr.append(WorkflowScopePrefix).append(wfid).append(":").append(scope);
- else
- scopestr.set(scope);
- wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scopestr, kind, description, value, 1, 0, StatsMergeReplace);
- }
- class WuTimingUpdater : implements ITimeReportInfo
- {
- public:
- WuTimingUpdater(IWorkUnit * _wu, StatisticScopeType _scopeType, StatisticKind _kind)
- : wu(_wu), scopeType(_scopeType), kind(_kind)
- { }
- virtual void report(const char * scope, const __int64 totaltime, const __int64 maxtime, const unsigned count)
- {
- wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, nullptr, totaltime, count, maxtime, StatsMergeReplace);
- }
- protected:
- IWorkUnit * wu;
- StatisticScopeType scopeType;
- StatisticKind kind;
- };
- extern WORKUNIT_API void updateWorkunitTimings(IWorkUnit * wu, ITimeReporter *timer)
- {
- WuTimingUpdater target(wu, SSTsection, StTimeTotalExecute);
- timer->report(target);
- }
- extern WORKUNIT_API void updateWorkunitTimings(IWorkUnit * wu, StatisticScopeType scopeType, StatisticKind kind, ITimeReporter *timer)
- {
- WuTimingUpdater target(wu, scopeType, kind);
- timer->report(target);
- }
- extern WORKUNIT_API void addTimeStamp(IWorkUnit * wu, StatisticScopeType scopeType, const char * scope, StatisticKind kind, unsigned wfid)
- {
- StringBuffer scopestr;
- if (wfid && scope && *scope)
- scopestr.append(WorkflowScopePrefix).append(wfid).append(":").append(scope);
- else
- scopestr.set(scope);
- wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scopestr, kind, NULL, getTimeStampNowValue(), 1, 0, StatsMergeAppend);
- }
- static double getCpuSize(const char *resourceName)
- {
- Owned<IPropertyTree> compConfig = getComponentConfig();
- const char * cpuRequestedStr = compConfig->queryProp(resourceName);
- if (!cpuRequestedStr)
- return 0.0;
- char * endptr;
- double cpuRequested = strtod(cpuRequestedStr, &endptr);
- if (cpuRequested < 0.0)
- return 0.0;
- if (*endptr == 'm')
- cpuRequested /= 1000;
- return cpuRequested;
- }
- static double getCostCpuHour()
- {
- double costCpuHour = 0.0;
- if (getComponentConfigSP()->hasProp("cost/@perCpu"))
- costCpuHour = getComponentConfigSP()->getPropReal("cost/@perCpu");
- else
- costCpuHour = getGlobalConfigSP()->getPropReal("cost/@perCpu");
- if (costCpuHour < 0.0)
- return 0.0;
- return costCpuHour;
- }
- extern WORKUNIT_API double getMachineCostRate()
- {
- unsigned numCpus = isContainerized() ? getCpuSize("resources/@cpu") : getAffinityCpus();
- return getCostCpuHour() * numCpus;
- }
- extern WORKUNIT_API double getThorManagerRate()
- {
- unsigned numCpus = isContainerized() ? getCpuSize("managerResources/@cpu") : getAffinityCpus();
- return getCostCpuHour() * numCpus ;
- }
- extern WORKUNIT_API double getThorWorkerRate()
- {
- unsigned numCpus = isContainerized() ? getCpuSize("workerResources/@cpu") : getAffinityCpus();
- return getCostCpuHour() * numCpus ;
- }
- extern WORKUNIT_API double calculateThorCost(unsigned __int64 ms, unsigned clusterWidth)
- {
- return calcCost(getThorManagerRate(), ms) + calcCost(getThorWorkerRate(), ms) * clusterWidth;
- }
- void aggregateStatistic(StatsAggregation & result, IConstWorkUnit * wu, const WuScopeFilter & filter, StatisticKind search)
- {
- SimpleReferenceAggregator aggregator(search, result);
- Owned<IConstWUScopeIterator> it = &wu->getScopeIterator(filter);
- ForEach(*it)
- it->playProperties(aggregator);
- }
- class GlobalStatisticGatherer : public CInterfaceOf<IStatisticGatherer>
- {
- public:
- GlobalStatisticGatherer(IWorkUnit * _wu) : wu(_wu) {}
- virtual void beginScope(const StatsScopeId & id)
- {
- prevLenStack.append(scope.length());
- if (scope.length())
- scope.append(":");
- id.getScopeText(scope);
- scopeTypeStack.append(id.queryScopeType());
- }
- virtual void beginSubGraphScope(unsigned id)
- {
- StatsScopeId scopeId(SSTsubgraph, id);
- beginScope(scopeId);
- }
- virtual void beginActivityScope(unsigned id)
- {
- StatsScopeId scopeId(SSTactivity, id);
- beginScope(scopeId);
- }
- virtual void beginEdgeScope(unsigned id, unsigned oid)
- {
- StatsScopeId scopeId(SSTedge, id, oid);
- beginScope(scopeId);
- }
- virtual void beginChildGraphScope(unsigned id)
- {
- StatsScopeId scopeId(SSTchildgraph, id);
- beginScope(scopeId);
- }
- virtual void beginChannelScope(unsigned id)
- {
- StatsScopeId scopeId(SSTchannel, id);
- beginScope(scopeId);
- }
- virtual void endScope()
- {
- scope.setLength(prevLenStack.popGet());
- scopeTypeStack.pop();
- }
- virtual void addStatistic(StatisticKind kind, unsigned __int64 value)
- {
- StatisticScopeType scopeType = scopeTypeStack.ordinality() ? (StatisticScopeType)scopeTypeStack.tos() : SSTglobal;
- wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, NULL, value, 1, 0, StatsMergeAppend);
- }
- virtual void updateStatistic(StatisticKind kind, unsigned __int64 value, StatsMergeAction mergeAction)
- {
- StatisticScopeType scopeType = scopeTypeStack.ordinality() ? (StatisticScopeType)scopeTypeStack.tos() : SSTglobal;
- wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, NULL, value, 1, 0, mergeAction);
- }
- virtual IStatisticCollection * getResult()
- {
- return NULL;
- }
- protected:
- Linked<IWorkUnit> wu;
- StringBuffer scope;
- UnsignedArray prevLenStack;
- UnsignedArray scopeTypeStack;
- };
- IStatisticGatherer * createGlobalStatisticGatherer(IWorkUnit * wu)
- {
- return new GlobalStatisticGatherer(wu);
- }
- extern WORKUNIT_API IPropertyTree * getWUGraphProgress(const char * wuid, bool readonly)
- {
- if (!wuid || !*wuid)
- return NULL;
- VStringBuffer path("/GraphProgress/%s", wuid);
- Owned<IRemoteConnection> conn = querySDS().connect(path.str(),myProcessSession(),readonly ? RTM_LOCK_READ : RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT);
- if (conn)
- return conn->getRoot();
- else
- return NULL;
- }
- void addWorkunitException(IWorkUnit * wu, IError * error, bool removeTimeStamp)
- {
- ErrorSeverity wuSeverity = SeverityInformation;
- ErrorSeverity severity = error->getSeverity();
- switch (severity)
- {
- case SeverityIgnore:
- return;
- case SeverityInformation:
- break;
- case SeverityWarning:
- wuSeverity = SeverityWarning;
- break;
- case SeverityError:
- case SeverityFatal:
- wuSeverity = SeverityError;
- break;
- }
- Owned<IWUException> exception = wu->createException();
- exception->setSeverity(wuSeverity);
- StringBuffer msg;
- exception->setExceptionCode(error->errorCode());
- exception->setExceptionMessage(error->errorMessage(msg).str());
- const char * source = queryCreatorTypeName(queryStatisticsComponentType());
- exception->setExceptionSource(source);
- exception->setExceptionFileName(error->getFilename());
- exception->setExceptionLineNo(error->getLine());
- exception->setExceptionColumn(error->getColumn());
- if (removeTimeStamp)
- exception->setTimeStamp(nullptr);
- if (error->getActivity())
- exception->setActivityId(error->getActivity());
- if (error->queryScope())
- exception->setScope(error->queryScope());
- }
- IError * WorkUnitErrorReceiver::mapError(IError * error)
- {
- return LINK(error);
- }
- void WorkUnitErrorReceiver::report(IError* eclError)
- {
- addWorkunitException(wu, eclError, removeTimeStamp);
- }
- size32_t WorkUnitErrorReceiver::errCount()
- {
- unsigned count = 0;
- Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
- ForEach(*exceptions)
- if (exceptions->query().getSeverity() == SeverityError)
- count++;
- return count;
- }
- size32_t WorkUnitErrorReceiver::warnCount()
- {
- unsigned count = 0;
- Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
- ForEach(*exceptions)
- if (exceptions->query().getSeverity() == SeverityWarning)
- count++;
- return count;
- }
- bool isValidPriorityValue(const char *priority)
- {
- if (isEmptyString(priority))
- return false;
- if (strieq("SLA", priority) || strieq("LOW", priority) || strieq("HIGH", priority) || strieq("NONE", priority))
- return true;
- return false;
- }
- bool isValidMemoryValue(const char *memoryUnit)
- {
- if (isEmptyString(memoryUnit) || !isdigit(*memoryUnit))
- return false;
- while (isdigit(*++memoryUnit));
- if (!*memoryUnit)
- return true;
- switch (toupper(*memoryUnit++))
- {
- case 'E':
- case 'P':
- case 'T':
- case 'G':
- case 'M':
- case 'K':
- if (!*memoryUnit || strieq("B", memoryUnit))
- return true;
- break;
- case 'B':
- if (!*memoryUnit)
- return true;
- break;
- }
- return false;
- }
- // used by eclagent and roxie
- #define ABORT_POLL_PERIOD (5*1000)
- void executeThorGraph(const char * graphName, IConstWorkUnit &workunit, const IPropertyTree &config)
- {
- unsigned timelimit = workunit.getDebugValueInt("thorConnectTimeout", config.getPropInt("@thorConnectTimeout", 60));
- StringAttr wuid(workunit.queryWuid());
- IConstWUGraph *graph = workunit.getGraph(graphName);
- if (!graph)
- throw makeStringExceptionV(0, "getGraph() returns nullptr for %s", graphName);
- unsigned wfid = graph->getWfid();
- #ifdef _CONTAINERIZED
- // NB: If a single agent were to want to launch >1 Thor, then the threading could be in the workflow above this call.
- {
- Owned<IWorkUnit> w = &workunit.lock();
- w->setState(WUStateBlocked);
- }
- WUState state = WUStateUnknown;
- if (config.hasProp("@queue"))
- {
- CCycleTimer elapsedTimer;
- bool multiJobLinger = config.getPropBool("@multiJobLinger");
- if (executeGraphOnLingeringThor(workunit, graphName, multiJobLinger ? config.queryProp("@queue") : nullptr))
- PROGLOG("Existing lingering Thor handled graph: %s", graphName);
- else
- {
- VStringBuffer queueName("%s.thor", config.queryProp("@queue"));
- DBGLOG("Queueing wuid=%s, graph=%s, on queue=%s, timelimit=%u seconds", wuid.str(), graphName, queueName.str(), timelimit);
- Owned<IJobQueue> queue = createJobQueue(queueName);
- VStringBuffer jobName("%s/%s", wuid.get(), graphName);
- IJobQueueItem *item = createJobQueueItem(jobName);
- queue->enqueue(item);
- }
- // NB: overall max runtime if guillotine set handled by abortmonitor
- unsigned runningTimeLimit = workunit.getDebugValueInt("maxRunTime", 0);
- runningTimeLimit = runningTimeLimit ? runningTimeLimit : INFINITE;
- std::list<WUState> expectedStates = { WUStateRunning, WUStateWait };
- unsigned __int64 blockedTime = 0;
- for (unsigned i=0; i<2; i++)
- {
- WUState state = waitForWorkUnitToComplete(wuid, timelimit*1000, expectedStates);
- DBGLOG("Got state: %s", getWorkunitStateStr(state));
- if (WUStateWait == state) // already finished
- {
- // workunit may have spent time in blocked state, but then transitioned to
- // wait state quickly such that this code did not see its running state.
- if (!blockedTime)
- blockedTime = elapsedTimer.elapsedNs();
- break;
- }
- else if ((INFINITE != timelimit) && (WUStateUnknown == state))
- throw makeStringExceptionV(0, "Query %s failed to start within specified timelimit (%u) seconds", wuid.str(), timelimit);
- else
- {
- auto it = std::find(expectedStates.begin(), expectedStates.end(), state);
- if (it == expectedStates.end())
- throw makeStringExceptionV(0, "Query %s failed, state: %s", wuid.str(), getWorkunitStateStr(state));
- }
- blockedTime = elapsedTimer.elapsedNs();
- timelimit = runningTimeLimit;
- expectedStates = { WUStateWait };
- }
- Owned<IWorkUnit> w = &workunit.lock();
- updateWorkunitStat(w, SSTgraph, graphName, StTimeBlocked, nullptr, blockedTime, wfid);
- }
- else
- {
- VStringBuffer job("%s-%s", wuid.str(), graphName);
- runK8sJob("thormanager", wuid, job, { { "graphName", graphName} });
- }
- /* In k8s, Thor feeds back the terminating exception via the workunit.
- * (in bare-metal it is returned via a socket the agent connect to Thor with)
- */
- workunit.forceReload();
- if (workunit.getExceptionCount())
- {
- Owned<IConstWUExceptionIterator> iter = &workunit.getExceptions();
- ForEach(*iter)
- {
- IConstWUException &e = iter->query();
- SCMStringBuffer str;
- e.getExceptionSource(str);
- if (streq("thormasterexception", str.s))
- {
- str.clear();
- e.getExceptionMessage(str);
- unsigned errCode = e.getExceptionCode();
- {
- /* Clear the feedback exception (consume it),
- * so that any future re-submissions of this workunit (e.g. due to workflow)
- * don't see it again.
- */
- Owned<IWorkUnit> w = &workunit.lock();
- w->clearExceptions("thormasterexception");
- }
- throw makeStringException(errCode, str.str());
- }
- }
- }
- else
- {
- Owned<IWorkUnit> w = &workunit.lock();
- WUState state = w->getState();
- if (WUStateFailed == state)
- throw makeStringException(0, "Workunit failed");
- }
- {
- Owned<IWorkUnit> w = &workunit.lock();
- w->setState(WUStateRunning);
- }
- #else
- StringAttr owner(workunit.queryUser());
- StringAttr cluster(workunit.queryClusterName());
- int priority = workunit.getPriorityValue();
- Owned<IConstWUClusterInfo> c = getTargetClusterInfo(cluster);
- if (!c)
- throw MakeStringException(0, "Invalid thor cluster %s", cluster.str());
- SCMStringBuffer queueName;
- c->getThorQueue(queueName);
- Owned<IJobQueue> jq = createJobQueue(queueName.str());
- bool resubmit;
- Owned<IWorkUnitFactory> wuFactory = getWorkUnitFactory();
- do // loop if pause interrupted graph and needs resubmitting on resume
- {
- resubmit = false; // set if job interrupted in thor
- if (WUStatePaused == workunit.getState()) // check initial state - and wait if paused
- {
- for (;;)
- {
- WUAction action = wuFactory->waitForWorkUnitAction(wuid, workunit.getAction());
- if (action == WUActionUnknown)
- throw new WorkflowException(0, "Workunit aborting", 0, WorkflowException::ABORT, MSGAUD_user);
- if (action != WUActionPause && action != WUActionPauseNow)
- break;
- }
- }
- {
- Owned<IWorkUnit> w = &workunit.lock();
- w->setState(WUStateBlocked);
- }
- class cPollThread: public Thread // MORE - why do we ned a thread here?
- {
- Semaphore sem;
- bool stopped;
- IJobQueue *jq;
- IConstWorkUnit *wu;
- public:
- bool timedout;
- CTimeMon tm;
- cPollThread(IJobQueue *_jq, IConstWorkUnit *_wu, unsigned timelimit)
- : tm(timelimit)
- {
- stopped = false;
- jq = _jq;
- wu = _wu;
- timedout = false;
- }
- ~cPollThread()
- {
- stop();
- }
- int run()
- {
- while (!stopped) {
- sem.wait(ABORT_POLL_PERIOD);
- if (stopped)
- break;
- if (tm.timedout()) {
- timedout = true;
- stopped = true;
- jq->cancelInitiateConversation();
- }
- else if (wu->aborting()) {
- stopped = true;
- jq->cancelInitiateConversation();
- }
- }
- return 0;
- }
- void stop()
- {
- stopped = true;
- sem.signal();
- }
- } pollthread(jq, &workunit, timelimit*1000);
- pollthread.start();
- CCycleTimer elapsedTimer;
- PROGLOG("Enqueuing on %s to run wuid=%s, graph=%s, timelimit=%d seconds, priority=%d", queueName.str(), wuid.str(), graphName, timelimit, priority);
- VStringBuffer wuidGraph("%s/%s", wuid.str(), graphName);
- IJobQueueItem* item = createJobQueueItem(wuidGraph.str());
- item->setOwner(owner.str());
- item->setPriority(priority);
- Owned<IConversation> conversation = jq->initiateConversation(item);
- bool got = conversation.get()!=NULL;
- pollthread.stop();
- pollthread.join();
- if (!got)
- {
- if (pollthread.timedout)
- throw MakeStringException(0, "Query %s failed to start within specified timelimit (%u) seconds", wuidGraph.str(), timelimit);
- throw MakeStringException(0, "Query %s cancelled (1)", wuidGraph.str());
- }
- // get the thor ep from whoever picked up
- SocketEndpoint thorMaster;
- MemoryBuffer msg;
- if (!conversation->recv(msg,1000*60))
- throw MakeStringException(0, "Query %s cancelled (2)", wuidGraph.str());
- thorMaster.deserialize(msg);
- msg.clear();
- SocketEndpoint myep;
- myep.setLocalHost(0);
- myep.serialize(msg); // only used for tracing
- if (!conversation->send(msg)) {
- StringBuffer s("Failed to send query to Thor on ");
- thorMaster.getUrlStr(s);
- throw MakeStringExceptionDirect(-1, s.str()); // maybe retry?
- }
- unsigned __int64 blockedTime = elapsedTimer.elapsedNs();
- {
- Owned<IWorkUnit> w = &workunit.lock();
- updateWorkunitStat(w, SSTgraph, graphName, StTimeBlocked, nullptr, blockedTime, wfid);
- }
- StringBuffer eps;
- PROGLOG("Thor on %s running %s", thorMaster.getUrlStr(eps).str(), wuidGraph.str());
- MemoryBuffer reply;
- try
- {
- if (!conversation->recv(reply,INFINITE))
- {
- StringBuffer s("Failed to receive reply from thor ");
- thorMaster.getUrlStr(s);
- throw MakeStringExceptionDirect(-1, s.str());
- }
- }
- catch (IException *e)
- {
- StringBuffer s("Failed to receive reply from thor ");
- thorMaster.getUrlStr(s);
- s.append("; (").append(e->errorCode()).append(", ");
- e->errorMessage(s).append(")");
- e->Release();
- throw MakeStringExceptionDirect(-1, s.str());
- }
- unsigned replyCode;
- reply.read(replyCode);
- switch ((ThorReplyCodes) replyCode)
- {
- case DAMP_THOR_REPLY_PAUSED:
- {
- bool isException;
- reply.read(isException);
- if (isException)
- {
- Owned<IException> e = deserializeException(reply);
- VStringBuffer str("Pausing job %s caused exception", wuidGraph.str());
- EXCLOG(e, str.str());
- }
- Owned<IWorkUnit> w = &workunit.lock();
- w->setState(WUStatePaused); // will trigger executeThorGraph to pause next time around.
- WUAction action = w->getAction();
- switch (action)
- {
- case WUActionPause:
- case WUActionPauseNow:
- w->setAction(WUActionUnknown);
- }
- resubmit = true; // JCSMORE - all subgraph _could_ be done, thor will check though and not rerun
- break;
- }
- case DAMP_THOR_REPLY_GOOD:
- break;
- case DAMP_THOR_REPLY_ERROR:
- {
- throw deserializeException(reply);
- }
- case DAMP_THOR_REPLY_ABORT:
- {
- Owned<IException> e = deserializeException(reply);
- StringBuffer msg;
- e->errorMessage(msg);
- throw new WorkflowException(e->errorCode(), msg.str(), 0, WorkflowException::ABORT, MSGAUD_user);
- }
- default:
- throwUnexpected();
- }
- workunit.forceReload();
- }
- while (resubmit); // if pause interrupted job (i.e. with pausenow action), resubmit graph
- #endif
- }
- #ifdef _CONTAINERIZED
- bool executeGraphOnLingeringThor(IConstWorkUnit &workunit, const char *graphName, const char *multiJobLingerQueueName)
- {
- // check if lingering thor instance is up.
- // Returns true if successfully submitted graph to a lingering Thor.
- /* If code was dependent on reading a workunit in parallel, the whole area of
- * workunit locking and reading will need revisiting, because at the moment the
- * workunit reading code does not lock at all.
- *
- * Either,
- * 1) multiJobThorLinger is set, meaning Thor instances will wait for any job's graph.
- * OR
- * 2) if not set, the Thor will wait for graphs from same job only, and will be
- * notified to quit by the agent, when the last graph is complete.
- *
- * This code is called from the client (agent) when it wants to execute a Thor graph.
- * This function checks to see if there any registered lingering Thor instances available.
- *
- * Lingering Thor's register when they are idle either with the workunit itself in
- * the case of [2], or with a Dali psuedo queue (a branch under /Status/ThorLinger),
- * in the case of [1]
- * If they have not been contacted within the 'lingerPeriod' they shutdown and remove
- * their registered instance from the queue.
- *
- * The client will attempt to find a lingering Thor in either the workunit or a global
- * named dali queue (depending on the configuration), it will then remove that entry,
- * and attempt to post the graph to that lingering Thor. If the Thor acknowledges the
- * request, this function exits with success. If it it fails, or there are no more
- * lingering Thor instances, it returns with false to indicate that the graph was not
- * submitted to a lingering Thor.
- * The client should then queue the wuid/graph normally, which will invoke a new Thor
- * instance.
- */
- try
- {
- if (multiJobLingerQueueName && graphName)
- {
- // check a Dali queue for idle spare Thor's
- DBGLOG("Checking multi job lingering queue: %s", multiJobLingerQueueName);
- VStringBuffer xpath("/Status/ThorLinger/%s", multiJobLingerQueueName);
- while (true)
- {
- Owned<IRemoteConnection> conn = querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE, 5*60*1000);
- if (!conn)
- break;
- IPropertyTree *root = conn->queryRoot();
- if (0 == root->numChildren())
- break;
- IPropertyTree *entry = root->queryPropTree("Thor*[1]");
- const char *entryName = entry->queryName();
- // get available instance and remove it
- VStringBuffer entryXPath("%s/%s", xpath.str(), entryName);
- Owned<IRemoteConnection> lingerInstanceConn = querySDS().connect(entryXPath, myProcessSession(), RTM_LOCK_WRITE | RTM_DELETE_ON_DISCONNECT, 5*1000);
- if (lingerInstanceConn)
- {
- StringBuffer instanceName;
- lingerInstanceConn->queryRoot()->getProp(nullptr, instanceName);
- lingerInstanceConn->close(true);
- lingerInstanceConn.clear();
- conn.clear();
- Owned<INode> masterNode = createINode(instanceName);
- CMessageBuffer msg;
- VStringBuffer jobStr("%s/%s", workunit.queryWuid(), graphName);
- msg.append(jobStr);
- bool success = queryWorldCommunicator().sendRecv(msg, masterNode, MPTAG_THOR, 10000);
- DBGLOG("Found lingering Thor: %s, submitted: %s, success: %s", instanceName.str(), jobStr.str(), boolToStr(success));
- if (success)
- {
- bool ok;
- msg.read(ok);
- return true;
- }
- }
- }
- }
- else
- {
- /* NB: forcing a reload here to ensure Debug values are recent.
- * Could be improved by refreshing only the specific area of interest (i.e. Debug/ in this case).
- * The area of persisting a non-locked connection to workunits should be resivisted..
- */
- workunit.forceReload();
- Owned<IStringIterator> iter = &workunit.getDebugValues("thorinstance_*");
- ForEach(*iter)
- {
- /* NB: Thor's set their running endpoint into Debug values of workunit
- * Check to see if workunit has any Thor's available lingering instances.
- */
- SCMStringBuffer thorInstance;
- iter->str(thorInstance);
- SCMStringBuffer thorInstanceValue;
- if (workunit.getDebugValueBool(thorInstance.s, false))
- {
- {
- Owned<IWorkUnit> w = &workunit.lock();
- w->setDebugValue(thorInstance.s, "0", true);
- }
- /* NB: there's a window where Thor could shutdown here.
- * In that case, the sendRecv will fail and it will fall through to queueing.
- */
- const char *instanceName = strchr(thorInstance.str(), '_') + 1;
- Owned<INode> masterNode = createINode(instanceName);
- CMessageBuffer msg;
- VStringBuffer jobStr("%s/%s", workunit.queryWuid(), graphName?graphName:"");
- msg.append(jobStr);
- if (queryWorldCommunicator().sendRecv(msg, masterNode, MPTAG_THOR, 10000))
- {
- bool ok;
- msg.read(ok);
- if (graphName) // if graphName==nullptr, then continue around to look at others
- {
- if (ok)
- return true;
- }
- }
- }
- }
- }
- }
- catch (IException *e)
- {
- EXCLOG(e, "executeGraphOnLingeringThor");
- e->Release();
- }
- return false;
- }
- static void setResources(IPropertyTree *workerConfig, const IConstWorkUnit *workunit, const char *process)
- {
- auto setResourcesItem = [&workerConfig](const char *category, const char *resourceName, unsigned value, const char *units)
- {
- if (!value) return;
- VStringBuffer xpath("spec/template/spec/containers/resources/%s@%s", category, resourceName);
- ensurePTree(workerConfig, xpath.str());
- VStringBuffer v("%u%s", value, units);
- workerConfig->setProp(xpath.str(), v.str());
- };
- StringBuffer s;
- unsigned memRequest = workunit->getDebugValueInt(s.clear().appendf("resource-%s-memory", process), 0);
- setResourcesItem("requests", "memory", memRequest, "Mi");
- setResourcesItem("limits", "memory", memRequest, "Mi");
- unsigned cpuRequest = workunit->getDebugValueInt(s.clear().appendf("resource-%s-cpu", process), 0);
- setResourcesItem("requests", "cpu", cpuRequest, "m");
- setResourcesItem("limits", "cpu", cpuRequest, "m");
- }
- KeepK8sJobs translateKeepJobs(const char *keepJob)
- {
- if (!isEmptyString(keepJob)) // common case
- {
- if (streq("podfailures", keepJob))
- return KeepK8sJobs::podfailures;
- else if (streq("all", keepJob))
- return KeepK8sJobs::all;
- }
- return KeepK8sJobs::none;
- }
- // NB: will fire an exception if command fails (returns non-zero exit code)
- static void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output)
- {
- StringBuffer _output, error;
- if (!output)
- output = &_output;
- unsigned ret = runExternalCommand(title, *output, error, cmd, input, ".", nullptr);
- if (output->length())
- MLOG(MCExtraneousInfo, unknownJob, "%s: ret=%u, stdout=%s", cmd, ret, output->trimRight().str());
- if (error.length())
- MLOG(MCinternalError, unknownJob, "%s: ret=%u, stderr=%s", cmd, ret, error.trimRight().str());
- if (ret)
- {
- if (input)
- MLOG(MCinternalError, unknownJob, "Using input %s", input);
- throw makeStringExceptionV(0, "Failed to run %s: error %u: %s", cmd, ret, error.str());
- }
- }
- bool isActiveK8sService(const char *serviceName)
- {
- VStringBuffer getEndpoints("kubectl get endpoints %s \"--output=jsonpath={range .subsets[*].addresses[*]}{.ip}{'\\n'}{end}\"", serviceName);
- StringBuffer output;
- runKubectlCommand("checkEndpoints", getEndpoints.str(), nullptr, &output);
- // Output should be zero or more lines each with an IP
- return (output.length() && output.charAt(0) != '\n');
- }
- void deleteK8sResource(const char *componentName, const char *job, const char *resource)
- {
- VStringBuffer jobname("%s-%s", componentName, job);
- jobname.toLowerCase();
- VStringBuffer deleteResource("kubectl delete %s/%s", resource, jobname.str());
- runKubectlCommand(componentName, deleteResource, nullptr, nullptr);
- }
- void waitK8sJob(const char *componentName, const char *job, unsigned pendingTimeoutSecs, KeepK8sJobs keepJob)
- {
- VStringBuffer jobname("%s-%s", componentName, job);
- jobname.toLowerCase();
- VStringBuffer waitJob("kubectl get jobs %s -o jsonpath={.status.active}", jobname.str());
- VStringBuffer getScheduleStatus("kubectl get pods --selector=job-name=%s --output=jsonpath={.items[*].status.conditions[?(@.type=='PodScheduled')].status}", jobname.str());
- VStringBuffer checkJobExitCode("kubectl get pods --selector=job-name=%s --output=jsonpath={.items[*].status.containerStatuses[?(@.name==\"%s\")].state.terminated.exitCode}", jobname.str(), jobname.str());
- unsigned delay = 100;
- unsigned start = msTick();
- bool schedulingTimeout = false;
- Owned<IException> exception;
- try
- {
- for (;;)
- {
- StringBuffer output;
- runKubectlCommand(componentName, waitJob, nullptr, &output);
- if (!streq(output, "1")) // status.active value
- {
- // Job is no longer active - we can terminate
- DBGLOG("kubectl jobs output: %s", output.str());
- runKubectlCommand(componentName, checkJobExitCode, nullptr, &output.clear());
- if (output.length() && !streq(output, "0")) // state.terminated.exitCode
- throw makeStringExceptionV(0, "Failed to run %s: pod exited with error: %s", jobname.str(), output.str());
- break;
- }
- runKubectlCommand(nullptr, getScheduleStatus, nullptr, &output.clear());
- // Check whether pod has been scheduled yet - if resources are not available pods may block indefinitely waiting to be scheduled, and
- // we would prefer them to fail instead.
- bool pending = streq(output, "False");
- if (pendingTimeoutSecs && pending && msTick()-start > pendingTimeoutSecs*1000)
- {
- schedulingTimeout = true;
- VStringBuffer getReason("kubectl get pods --selector=job-name=%s \"--output=jsonpath={range .items[*].status.conditions[?(@.type=='PodScheduled')]}{.reason}{': '}{.message}{end}\"", jobname.str());
- runKubectlCommand(componentName, getReason, nullptr, &output.clear());
- throw makeStringExceptionV(0, "Failed to run %s - pod not scheduled after %u seconds: %s ", jobname.str(), pendingTimeoutSecs, output.str());
- }
- MilliSleep(delay);
- if (delay < 10000)
- delay = delay * 2;
- }
- }
- catch (IException *e)
- {
- EXCLOG(e, nullptr);
- exception.setown(e);
- }
- if (keepJob != KeepK8sJobs::all)
- {
- // Delete jobs unless the pod failed and keepJob==podfailures
- if ((nullptr == exception) || (KeepK8sJobs::podfailures != keepJob) || schedulingTimeout)
- deleteK8sResource(componentName, job, "job");
- }
- if (exception)
- throw exception.getClear();
- }
- 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)
- {
- StringBuffer jobname(job);
- jobname.toLowerCase();
- VStringBuffer jobSpecFilename("/etc/config/%s-%s.yaml", componentName, suffix);
- StringBuffer jobYaml;
- try
- {
- jobYaml.loadFile(jobSpecFilename, false);
- }
- catch (IException *E)
- {
- if (!optional)
- throw;
- E->Release();
- return false;
- }
- jobYaml.replaceString("_HPCC_JOBNAME_", jobname.str());
- VStringBuffer args("\"--workunit=%s\"", wuid);
- for (const auto &p: extraParams)
- {
- if (hasPrefix(p.first.c_str(), "_HPCC_", false)) // jobspec substituion
- jobYaml.replaceString(p.first.c_str(), p.second.c_str());
- else
- args.append(" \"--").append(p.first.c_str()).append('=').append(p.second.c_str()).append("\"");
- }
- jobYaml.replaceString("_HPCC_ARGS_", args.str());
- // Disable ability change resources from within workunit
- // - all values are unquoted by toYAML. This caused problems when previous string values are
- // outputted unquoted and then treated as a non string type -e.g. labels in metadata.
- // - Also, ability to control if and how much users may change resources should be provided.
- #if 0
- Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
- if (factory)
- {
- Owned<IConstWorkUnit> workunit = factory->openWorkUnit(wuid);
- if (workunit)
- {
- Owned<IPropertyTree> workerConfig = createPTreeFromYAMLString(jobYaml.length(), jobYaml.str(), 0, ptr_none, nullptr);
- setResources(workerConfig, workunit, componentName);
- toYAML(workerConfig, jobYaml.clear(), 2, 0);
- }
- }
- #endif
- runKubectlCommand(componentName, "kubectl replace --force -f -", jobYaml, nullptr);
- return true;
- }
- static constexpr unsigned defaultPendingTimeSecs = 600;
- void runK8sJob(const char *componentName, const char *wuid, const char *job, const std::list<std::pair<std::string, std::string>> &extraParams)
- {
- Owned<IPropertyTree> compConfig = getComponentConfig();
- KeepK8sJobs keepJob = translateKeepJobs(compConfig->queryProp("@keepJobs"));
- unsigned pendingTimeoutSecs = compConfig->getPropInt("@pendingTimeoutSecs", defaultPendingTimeSecs);
- bool removeNetwork = applyK8sYaml(componentName, wuid, job, "networkspec", extraParams, true);
- applyK8sYaml(componentName, wuid, job, "jobspec", extraParams, false);
- Owned<IException> exception;
- try
- {
- waitK8sJob(componentName, job, pendingTimeoutSecs, keepJob);
- }
- catch (IException *e)
- {
- EXCLOG(e, nullptr);
- exception.setown(e);
- }
- if (removeNetwork)
- deleteK8sResource(componentName, job, "networkpolicy");
- if (exception)
- throw exception.getClear();
- }
- std::pair<std::string, unsigned> getExternalService(const char *serviceName)
- {
- static CTimeLimitedCache<std::string, std::pair<std::string, unsigned>> externalServiceCache;
- static CriticalSection externalServiceCacheCrit;
- {
- CriticalBlock b(externalServiceCacheCrit);
- std::pair<std::string, unsigned> cachedExternalSevice;
- if (externalServiceCache.get(serviceName, cachedExternalSevice))
- return cachedExternalSevice;
- }
- StringBuffer output;
- try
- {
- 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);
- runKubectlCommand("get-external-service", getServiceCmd, nullptr, &output);
- }
- catch (IException *e)
- {
- EXCLOG(e);
- VStringBuffer exceptionText("Failed to get external service for '%s'. Error: [%d, ", serviceName, e->errorCode());
- e->errorMessage(exceptionText).append("]");
- e->Release();
- throw makeStringException(-1, exceptionText);
- }
- StringArray fields;
- fields.appendList(output, ",");
- // NB: add even if no result, want non-result to be cached too
- std::string host, port;
- if (fields.ordinality() == 3) // hostname,ip,port. NB: hostname may be missing, but still present as a blank field
- {
- host = fields.item(0); // hostname
- if (0 == host.length())
- host = fields.item(1); // ip
- port = fields.item(2);
- }
- auto servicePair = std::make_pair(host, atoi(port.c_str()));
- externalServiceCache.add(serviceName, servicePair);
- return servicePair;
- }
- // returns a vector of {pod-name, node-name} vectors,
- // represented as a nested vector for extensibility, e.g. to add other meta fields
- std::vector<std::vector<std::string>> getPodNodes(const char *selector)
- {
- VStringBuffer getWorkerNodes("kubectl get pods --selector=job-name=%s \"--output=jsonpath={range .items[*]}{.metadata.name},{.spec.nodeName}{'\\n'}{end}\"", selector);
- StringBuffer result;
- runKubectlCommand("get-worker-nodes", getWorkerNodes, nullptr, &result);
- if (result.isEmpty())
- throw makeStringExceptionV(-1, "No worker nodes found for selector '%s'", selector);
- const char *start = result.str();
- const char *finger = start;
- std::string fieldName;
- std::vector<std::vector<std::string>> results;
- std::vector<std::string> current;
- while (true)
- {
- switch (*finger)
- {
- case ',':
- {
- if (start == finger)
- throw makeStringException(-1, "getPodNodes: Missing node name(s) in output");
- fieldName.assign(start, finger-start);
- current.emplace_back(std::move(fieldName));
- finger++;
- start = finger;
- break;
- }
- case '\n':
- case '\0':
- {
- if (start == finger)
- throw makeStringException(-1, "getPodNodes: Missing pod name(s) in output");
- fieldName.assign(start, finger-start);
- current.emplace_back(std::move(fieldName));
- results.emplace_back(std::move(current));
- if ('\0' == *finger)
- return results;
- finger++;
- start = finger;
- break;
- }
- default:
- {
- ++finger;
- break;
- }
- }
- }
- }
- #endif
|