VISOKOŠOLSKI STROKOVNI ŠTUDIJ Računalništvo in Informacijske Tehnologije POROČILO PRAKTIČNEGA IZOBRAŽEVANJA V Cloudkick, Inc. - San Francisco, Kalifornija, Združene Države Amerike Čas opravljanja od 26.09.2010 do 10.01.2011 Mentor v GD Paul Querna Študent Tomaž Muraus Vpisna številka E1009462 E pošta tomaz+feri@tomaz.me Telefon +386 40 389 540
1 POGODBA O PRAKTIČNEM USPOSABLJANJU 2 / 37
2 KAZALO Kazalo vsebine 1 Pogodba o praktičnem usposabljanju... 2 2 Kazalo...3 3 Uvod... 5 4 Opis gospodarske družbe in produktov... 6 4.1 Opis produkta...10 5 Opis praktičnega izobraževanja...13 6 Strokovno področje ali projekt... 15 6.1 Izdelava vtičnika za agenta... 15 Uvod in arhitektura agenta...15 Izdelava vtičnika... 18 Testiranje vtičnika... 20 6.2 Cast... 21 Uvod...21 Arhitektura... 25 Razvoj... 26 7 Sklep... 29 8 Povezave...30 9 Priloge...32 9.1 Priloga 1: Izvorna koda vtičnika...32 9.2 Priloga 2 izvorna koda modula s pomožnimi funkcijami za testiranje...36 Kazalo slik Slika 1: Logotip podjetja pred prevzemom... 6 3 / 37
Slika 2: Logotip podjetja po prevzemu...6 Slika 3: libcloud.org - spletna stran projekta libcloud, kjer so našteti podprti ponudniki...9 Slika 4: cloudkick.com - vstopna točka oziroma nadzorna plošča...10 Slika 5: cloudkick.com - pogleda posameznega strežnika...11 Slika 6: Arhitektura agenta... 16 Slika 7: Cast - izpis pri uporabi ukaza "info"... 20 Slika 8: Arhitektura sistema Cast... 22 Slika 9: Dokumentacija generirana s pomočjo orodja JSDoc...23 Slika 10: github.com - prikaz komentarja na spremembi (diff)...24 4 / 37
3 UVOD Svojo prakso sem opravljal oddaljeno v podjetju Cloudkick z sedežem in pisarno v Kaliforniji. Ker je podjetje v času začetka opravljanja moje prakse bilo še manjše (okoli 10 redno zaposlenih) to pomeni, da še ni bilo razdeljeno v več pod-enot in je več zaposlenih delalo na več različnih delih produkta. Tudi sam sem spoznal in vsaj malo sodeloval pri vseh delih produkta. V tem poročilu sem kot projektno delo opisal izdelavo vtičnika za agenta in sodelovanje pri odprto-kodnem projektu za distribucijo, nameščanje in posodabljanje programske kode in aplikacij imenovanem Cast. 5 / 37
4 OPIS GOSPODARSKE DRUŽBE IN PRODUKTOV Cloudkick je bilo v času, ko sem začel z praktičnim usposabljanjem še manjše oziroma tako imenovano startup podjetje z pisarno v Kaliforniji. Leta 2009 je bilo ustanovljeno z strani treh študentov univerze v Oregonu - Alex Polvi, Dan Di Spaltro in Logan Weliver. Velik preobrat se je zgodil decembra 2010, ko je podjetje bilo prevzeto z strani drugega podjetja Rackspace. Rackspace[2] je eno izmed največjih in najstarejših podjetij, ki se ukvarja z gostovanjem in zraven Amazona tudi drugo največje podjetja, ki se ukvarja z nudenjem in prodajanjem storitev povezanih z računalništvom v oblaku. Trenutno je v podjetju Rackspace skupaj zaposlenih okoli 3500 delavcev. Sedež podjetja se nahaja v Teksasu, pisarne podjetja pa se nahajajo tudi na Nizozemskem, Angliji, Avstraliji, Hong Kongu in po prevzemu tudi v San Francisco-u. Slika 1: Logotip podjetja pred prevzemom Slika 2: Logotip podjetja po prevzemu Glavni produkt podjetja je istoimenska spletna aplikacija (SaaS) za upravljanje in nadzor oblačnih (cloud), virtualnih (vps) in klasičnih (dedicated) strežnikov. Moja naloga v podjetju je bila izpopolnjevanje in nadgrajevanje obstoječe spletne aplikacije in pripadajočih servisov ter sodelovanje pri izdelavi odprto-kodnega projekta za distribucijo, posodabljanje in nameščanje aplikacij imenovanega Cast. Pri razvoju glavnega produkta se uporablja veliko različnih programskih jezikov, 6 / 37
tehnologij in orodij. Primarni programski jezik za spletno aplikacijo in storitve je Python, velik del kode na front-u pa je napisan v jeziku JavaScript. Zraven teh dveh jezikov še kar zajeten del kode predstavlja jezik C v katerem je napisan agent, ki se namesti na nadzorovan strežnik, jezik Lua[18] v katerem so napisane določene skripte in vtičniki za agenta ter jezika C++ in Java, ki se uporabljata v nekaterih internih orodjih in storitvah. Večina orodij in tehnologij, ki se uporabljajo v podjetju so prosto dostopne in izdane pod različnimi odprto-kodnimi licencami (Apache 2.0, BSD, MIT, GPL). Nekaj primerov uporabljenih tehnologij in orodij: Django[3] odprto-kodno ogrodje za izdelavo dinamičnih spletnih aplikacij napisano v programskem jeziku Python Twisted[4] odprto-kodno ogrodje napisano v jeziku Python namenjeno izdelavi raznih omrežnih storitev temelječih na asinhronem pristopu Cassandra[5] odprto-kodna podatkovna baza, katere pristopi so pobrani iz Amazon-ove podatkovne baze Dynamo in Google-ove podatkovne baze BigTable Google Closure[6] odprto-kodno ogrodje napisano v jeziku JavaScript namenjeno izdelavi bogatih interaktivnih spletnih aplikacij Scribe[7] odprto-koden strežnik za agregacijo in transformacijo dnevnikov s strani velikega števila strežnikov Solr[8] odprto-koden strežnik za full-text iskanje temelječ na ogrodju Apache Lucence MySQL[9] odprto-kodna relacijska podatkovna baza Apache httpd[10] odprto-koden spletni strežnik RabbitMQ[11] odprto-koden messaging strežnik (MOM) napisan v programskem jeziku Erlang txamqp[12] odprto-kodna knjižnica napisana v programskem jeziku Python namenjena komunikaciji s messaging strežnikom preko protokola AMQP 7 / 37
Esper[13] odprto-koden strežnik za procesiranje dogodkov (complex event processing system) NodeJS[23] ogrodje namenjeno izdelavi visoko-zmogljivih aplikacij napisanih v jeziku JavaScript temelječih na asinhronem pristopu in še veliko drugih Podjetje pa zraven uporabe pri nekaterih projektih tudi aktivno sodeluje. Sodelovanje poteka na različne načine; pomoč z testiranjem in poročanjem napak, izdelava popravkov za razne hrošče in odpiranje lastnih modifikacij za uporabljene projekte ter knjižnjice. Primeri projektov, kjer je podjetje sodelovalo na enega ali več izmed zgoraj opisanih načinov so; Apache Cassandra, Twisted, txamqp, NodeJS. Podjetje je za lastne potrebe tudi razvilo in kasneje odprlo knjižnico libcloud[14]. Ta knjižnica je nastala zaradi potrebe po standardizaciji in poenotenju aplikacijskih vmesnikov ponudnikov storitev v oblaku. Knjižnica je torej nekaj vrste abstrakcija vmesnikov vseh teh ponudnikov storitev v oblaku. Trenutno podpira več kot 15 različnih ponudnikov, med njimi pa so tudi največji ponudniki kot so Amazon, Rackspace in Linode. 8 / 37
Slika 3: libcloud.org - spletna stran projekta libcloud, kjer so našteti podprti ponudniki 9 / 37
4.1 Opis produkta Cloudkick[1] spletna aplikacija (SaaS) za nadzor in upravljanje oblačnih in navadnih strežnikov. Slika 4: cloudkick.com - vstopna točka oziroma nadzorna plošča Glavni del viden uporabnikom je spletna nadzorna plošča, ki uporabniku na pregleden in kompakten način prikaže trenutno stanje njegovih strežnikov. Zraven pregledne nadzorne plošče, ki deluje kot vstopna točka pa aplikacija ponuja tudi veliko druge funkcionalnosti: napredni alarmi, ki uporabniku omogočajo, da si nastavi različne meje o katerih bo obveščen preko e-pošte, tekstovnega sporočila (SMS), Webhook-a ali preko sistema PagerDuty[15] 10 / 37
program CKL, ki administratorjem strežnikov omogoča snemanje sej in beleženje sporočil, ki so nato vidna na grafih v spletnem vmesniku (ta sporočila se se v večini primerov uporabljajo kot neke vrste opombe) API vmesnik, ki drugih razvijalcem omogoča, da na naši platformi gradijo lastne aplikacije Cloudkick Viz[16] sistem, ki uporabnikom omogoča 3D pogled stanja strežnikov v realnem času izdelava lastnih vtičnikov za agenta, ki so lahko napisani v poljubnem programskem jeziku Slika 5: cloudkick.com - pogleda posameznega strežnika 11 / 37
Cast[17] sistem za hitro in varno distribucijo, nameščanje in posodabljanje programske kode ter aplikacij Cast je primarno namenjen podjetjem, ki imajo v lasti večje število strežnikov in željo na njih namestiti svojo programsko kodo in razne aplikacije. Sistem je na voljo zastonj in je izdan pod prosto licenco Apache 2.0. Projekt je še trenutno v zgodnjem razvoju, izvorna koda pa se nahaja na Github-u https://github.com/cloudkick/cast. 12 / 37
5 OPIS PRAKTIČNEGA IZOBRAŽEVANJA Sam sem pri praktičnem izobraževanju vsaj delno sodeloval skoraj pri vseh delih produkta: izdelava vtičnikov za agenta v jeziku Lua izboljšave in popravki v sami izvorni kodi agenta (programski jezik C) sodelovanje pri izdelavi spletne aplikacije (programski jezik Python in ogrodje Django) sodelovanji pri izdelavi različnih storitev (programski jezik Python in ogrodje Twisted) vzdrževanje in konfiguracija velikega števila strežnikov (operacijski sistem Ubuntu, sistem za upravljanje konfiguracije Puppet) sodelovanje pri izdelavi odpro-kodnega projekta za distribucijo in nameščanje aplikacij imenovanega Cast (primarno je Cast napisan v programskem jeziku JavaScript, sistem Scons za namestitev pa je napisan v programskem jeziku Python) Za komunikacijo med delom se v podjetju primarno uporablja IRC kanal, za razne pomembnejše sestanke pa se v večini primerov uporablja video konferenca in program Skype. Manjša ovira pri mojem praktičnem izobraževanju je bila časovna razlika (9 ur), kar pa se je kasneje izkazalo za koristno. Takrat, ko je večina sodelavcev iz Kalifornije spalo sem jaz bil buden kar je pomenilo, da sem lahko v tistem času pomagal tudi pri podpori in raznih incidentih in težavah, ki so se pojavili na naših strežnikih. Za začetek razvoja in aktivnega sodelovanja sem kot prvo moral na svojem prenosniku vzpostavitvi razvojno okolje. Ker se pri delu uporablja veliko število tehnologij in aplikacij je sama vzpostavitev delovnega okolja relativno težavna in vzame kar veliko časa. Meni 13 / 37
osebno je postavitev dela delovnega okolja prvič vzela skoraj cel dan. Po vzpostavitvi delovnega okolja in deloma pred ter med pa sem se počasi tudi začel spoznavati z komponentami sistema, obstoječo izvorno kodo in interno dokumentacijo, ki se nahaja na sistemu wiki. Same izvorne kode je ogromno zato sem na začetku veliko stvari vzel kot črno škatlo, ker sem prvo poskušal razumeti kako je sistem sestavljen in kako deluje kot celota. Kasneje ob raznih zadolžitvah pa sem se poglobil tudi v samo drobovje različnih storitev, tako da sem na koncu praktičnega izobraževanja imel že dobro poglobljeno razumevanje celotnega sistema. 14 / 37
6 STROKOVNO PODROČJE ALI PROJEKT 6.1 Izdelava vtičnika za agenta Uvod in arhitektura agenta Med praktičnim izobraževanjem sem izdelal kar nekaj vtičnikov (check plugin) za agenta, eden izmed njih pa je bil vtičnik za nadzorovanje podatkovne baze Redis[19]. Vsi vtičniki so brez stanja (stateless), njihova naloga pa je, da zbirajo podatke oziroma metrike, katere agente pošlje našim strežnikom. Redis je NoSQL podatkovna baza, ki za razliko od relacijske baze za podatkovni model ne uporablja tabel (stolpci in vrstice) ampak osnovne podatkovne strukture[20]. Primeri teh podatkovnih struktur so: seznam (list), množica (set), urejena množica (sorted set), slovar (hash), vrsta (queue), ipd. Za dostop in urejanje podatkov ne uporabljamo povpraševalnega jezika SQL ampak posebne knjižice, ki so na voljo za večino popularnih programskih jezikov. Večina teh knjižnic pa ima možnost, da te podatkovne strukture razvijalcu izpostavi kot nativne podatkovne strukture tega programskega jezika. Za izdelavo vtičnika sem najprej moral prenesti izvorno kodo agenta in drugih vtičnikov napisanih v programskem jeziku Lua. Lua je skriptni jezik katerega gramatika je relativno preprosta, namenjen pa je za uporabo v raznih vgrajenih napravah z omejenimi viri in drugih programih, kjer se uporablja kot sekundaren skriptni jezik Lua je posebej popularen in uporabljen kot skripti jezik pri grah. Ena izmed iger, ki za izdelavo vtičnikov in razširitev uporablja ta programski jezik je igra World of Warcraft. Pri agentu se je prvoten razvijalec za jezik Lua odločil zato, ker je izdelati vtičnik v programskem jeziku Lua veliko lažje (jezik je interpretiran in dinamično pisan,...) in varneje (ni treba skrbeti za ročno sproščanje pomnilnika, ker ima Lua vgrajenega smetara - Garbage Collector ) kot pa v programskem jeziku C v katerem je napisan sam agent. 15 / 37
Agent deluje tako, da na vsake nekaj časa (privzeto je to 30 sekund) v novi niti zažene enega izmed vtičnikov, ki zbere za nas zanimive podatke nato pa jih pošlje našim strežnikom, ki te podatke shranijo v podatkovno bazo Cassandra. Seveda pa se pred podatkovno bazo nahaja tudi plast za pred-pomnjenje in strežnik memcache, v katerem se po principu write-through shranjujejo metrike in drugi pogosto dostopani podatki. Podatki so iz predpomnilnika odstranjeni po principu LRU. Slika 6: Arhitektura agenta 16 / 37
Izdelava vtičnika Za verzioniranje kode se v podjetju uporablja sistem Git, tako da sem skladišče z izvorno kodo preprosto kloniral na svoj računalnik. Ko je izvorna koda bila klonirana sem se naprej spoznal z delovanjem in izvorno kodo agenta ter obstoječih Lua vtičnikov. Ko sem se spoznal z delovanjem strežnika sem se odpravil na uradno stran projekta Redis, kjer sem dobro preučil dokumentacijo za ukaz INFO [21]. Ukaz INFO vrne razne informacije o Redis strežniku in statistiko o podatkih, ki so trenutno shranjeni. Tukaj je primer izpisa, ki ga vrne ukaz INFO : INFO $988 redis_version:2.0.4 redis_git_sha1:5c3fd23b redis_git_dirty:0 arch_bits:64 multiplexing_api:kqueue process_id:1726 uptime_in_seconds:6263145 uptime_in_days:72 connected_clients:3 connected_slaves:0 blocked_clients:0 used_memory:22949296 used_memory_human:21.89m changes_since_last_save:24 bgsave_in_progress:0 last_save_time:1297039444 bgrewriteaof_in_progress:0 total_connections_received:7269809 total_commands_processed:22781678 expired_keys:45074 hash_max_zipmap_entries:64 17 / 37
hash_max_zipmap_value:512 pubsub_channels:0 pubsub_patterns:0 vm_enabled:1 role:master vm_conf_max_memory:6000000000 vm_conf_page_size:32 vm_conf_pages:134217728 vm_stats_used_pages:0 vm_stats_swapped_objects:0 vm_stats_swappin_count:0 vm_stats_swappout_count:0 vm_stats_io_newjobs_len:0 vm_stats_io_processing_len:0 vm_stats_io_processed_len:0 vm_stats_io_active_threads:0 vm_stats_blocked_clients:0 db0:keys=4,expires=0 db1:keys=641,expires=0 db2:keys=453,expires=49 Kot je razvidno je format preprost in omogoča enostavno parsanje ključi so med seboj ločeni z znakom za novo vrstico (\n), ključ in vrednost pa sta med seboj ločena z dvopičjem (:). Sam ukaz vrne veliko količino podatkov, za nas pa so zanimivi samo nekateri, zato sem sparsal in shranil samo tiste, ki nas zanimajo. Pri izdelavi vtičnika sem si pomagal tudi z dokumentacijo programskega jezika Lua. Pred izdelavo vtičnikov v programskem jeziku Lua nisem naredil še nobenega programa ampak mi učenje ni predstavljajo večjih težav, ker je sama sintaksa večinoma podobna programskemu jeziku Python. Vtičnik deluje tako, da vzpostavi TCP povezavo z Redis strežnikom, pošlje ukaz INFO, iz odgovora sparsa podatke, ki so za nas zanimivi in na koncu zapre TCP povezavo. 18 / 37
Testiranje vtičnika Ko sem vtičnik izdelal sem prvo izvedel nekaj ročnih testov in sicer tako, da sem si na svoj računalnik namestil podatkovno bazo Redis, nato pa sem prevedel agenta in zagnal izdelan vtičnik ter preveril, če se rezultat ujema z pričakovanim. Seveda je bil ročen test samo prvi korak. Po ročnem testiranju pa sem napisal tudi avtomatske teste. Za funkcijsko testiranje Lua vtičnikov se v podjetju uporablja ogrodje Lunit[22]. Izdelal sem dva testa; prvi je klasični funkcijski test, ki testira pravilnost funkcij, ki parsajo podatke, drugi pa je test celotne funkcionalnosti, torej neke vrste integracijski test. Da sem lahko izdelal drugi test sem v programskem jeziku Lua napisal tudi preprost TCP strežnik, kateremu se ob zagonu poda slovar ukazov in odgovorov. Slovar pove kako se bo strežnik ob prihajajočem ukazu odzval. Primer vhodnega slovarja, ki se poda testnemu strežniku: { "INFO": "uptime:287878\nconnected_clients:10\nused_memory:1245567" } Ključ je v tem primeru INFO, vrednost pa je niz, ki se nahaja med narekovaji. 19 / 37
6.2 Cast Uvod Kot drugi del pri projektnem delu pa sem sodeloval pri izdelavi odprto-kodnega sistema za razširjanje, nameščanje in posodabljanje programske kode in aplikacij. Slika 7: Cast - izpis pri uporabi ukaza "info" Sistem je primarno namenjen podjetjem, ki imajo v lasti veliko strežnikov in želijo na njih hitro, varno in zanesljivo namestiti neko aplikacijo ali novo različico izvorne kode lastne aplikacije. Primer takšnega podjetja je na primer Facebook, ki želi na svoje strežnike ali del strežnikov namestiti novo različico spletne aplikacije ali pa želi nadgraditi MySQL podatkovno bazo na določenih strežnikih. 20 / 37
Sistem je napisan v programskem jeziku JavaScript, kot temelj pa uporablja ogrodje NodeJS, ki je primarno namenjeno izdelavi zanesljivih in visoko-zmogljivih omrežnih storitev. Preden sem začel sodelovati pri tem projektu sem z programskim jezikom JavaScript že imel kar nekaj izkušenj zaradi razvoja raznih client-side rešitev ampak v tem primeru je bil JavaScript uporabljen kot server-side jezik. JavaScript je zelo poseben jezik zaradi tega, ker ima zraven dobrih delov (funkcije so prvo-razredni meščani, delno se lahko uporablja kot funkcijski jezik, itd.) tudi veliko slabih, zato je pri razvoju zelo treba paziti katere dele jezika in le-kako se ti uporabljajo. Pred začetkom razvoja sem jaz in ostali, ki sodelujejo pri tem projektu imeli tudi kratko predavanje o pasteh jezika JavaScript s strani Bjorn-a, ki je pri podjetju Cloudkick zaposlen kot front razvijalec. Sam sem malo po začetku razvoja tudi prebral knjigo JavaScript The Good Parts [24] od Douglasa Crockford-a, ki ravno tako kot sem omenil zgoraj opisuje kako uporabiti dobre dele tega jezika, čemu se je treba izogniti in kje so potencialne pasti. 21 / 37
Arhitektura Sistem Cast je sestavljen iz dveh delov odjemalca (client), ki se ponavadi nahaja na računalniku upravitelja oziroma uporabnika in agenta, ki se nahaja na strežnikih in posluša na ukaze poslane z strani odjemalca ali drugih agentov. Slika 8: Arhitektura sistema Cast Trenutno je odjemalec enako kot agent napisan v programskem jeziku JavaScript ampak, ker sama filozofija projekta temelji na tem, da je sistem sestavljen modularno in vso svojo funkcionalnost izpostavi kot spletno storitev preko dobro zastavljenega aplikacijskega vmesnika to pomeni, da se kasneje odjemalec lahko izdela v praktično katerem koli programskem jeziku. 22 / 37
Razvoj Na začetku sva jaz in drugi sodelavec, ki je sodeloval pri tem projektu poiskala različne obstoječe knjižnjice, ki jih bomo pri projektu tudi uporabili. Ker je projekt izdan pod licenco Apache 2.0 to pomeni, da morejo tudi licence ostalih komponent biti kompatibilne s to licenco. Kot primer kompatibilne licence sta licenci BSD in MIT. Uporabijo se lahko tudi komponente izdane pod licenco GPL, ampak to komponente ne smemo distribuirati z našo aplikacijo. Primer take komponente je na primer knjižnica za izdelavo dokumentacije iz komentarjev v kodi, ki se uporabi samo v razvoju kadar se generira dokumentacija. S kolegom sva iskala takšne knjižnice, ki so v aktiven razvoju in imajo za seboj raznoliko in aktivno skupnost. Seveda je tukaj tudi ena majhna past platforma NodeJS je relativno mlada (prva javna različica je izšla leta 2009), kar pomeni, da še na žalost za vsako stvar, ki bi želel ne obstaja dovolj kvalitetna in vzdrževana knjižnjica. Za začetek sva našla knjižnico za testiranje Expreso, sistem za generiranje dokumentacije in komentarjev v kodi JSDoc in knjižnico za kontrolo toka imenovano async. Slika 9: Dokumentacija generirana s pomočjo orodja JSDoc 23 / 37
Ko sva našla nekaj osnovnih komponent, ki nam bodo pomagale pri razvoju sva začela z razvojem. Delo sva si razdelila tako, da je vsak naredil komponento ali del neke komponente. Zraven dela sva bila tudi dosti v stiku preko IRC-a in Skype-a, kjer sva si izmenjavala komentarje in mnenja. Ker je izvorna koda gostovana na Github-u pomeni, da sva lahko uporabila tudi vgrajen sistem za komentiranje sprememb (code reviews). Slika 10: github.com - prikaz komentarja na spremembi (diff) Zraven konstantne komunikacije med nama pa sva tudi trikrat mesečno oziroma po potrebi imela sestanek z Paul-om, ki je vodja tega projekta. Z Paul sva se pogovarjala o samih ciljih projekta in tudi o podrobnostih implementacije 24 / 37
kakšne komponente oziroma problema. Kot sem napisal že na začetku je projekt še v zgodnji fazi in aktivnem razvoju, kar pomeni, da se večje spremembe v tej fazi dogajajo tedensko, razvoju pa se najlažje sledi na Github-u. 25 / 37
7 SKLEP Zahvalil bi se podjetju Cloudkick, ki mi je omogočilo praktično izobraževanje. Veliko tehnologij in produktov, ki se uporabljajo v podjetju sem sicer vsaj delno poznal že prej ampak tukaj sem svoje znanje pri določenih tehnologijah še dodatno poglobil. Dobil sem tudi veliko praktičnih izkušenj pri nadzorovanju velikega števila strežnikov in obdelavi velike količine podatkov. Popolnoma drugače je, če kakšno stvar testiraš sam na nekaj strežnikih ali pa v produkciji s 150 in več strežniki ter veliko količino podatkov, ki vsak dan strmo narašča. Zraven pridobitve velike količine tehničnega znanja pa sem dobil pomembne izkušnje na področju sodelovanja v ekipi. 26 / 37
8 POVEZAVE [1] Cloudkick spletna stran - http://www.cloudkick.com [2] Rackspace spletna stran - http://www.rackspace.com [3] Django ogrodje - http://www.djangoproject.com [4] Twisted ogrodje - http://www.twistedmatrix.com [5] Cassandra podatkovna baza - http://cassandra.apache.org [6] Google Closure ogrodje - http://code.google.com/closure [7] Scribe - https://github.com/facebook/scribe [8] Solr - http://lucene.apache.org/solr [9] Mysql - http://www.mysql.com [10] Apache httpd - http://httpd.apache.org [11] RabbitMQ - http://www.rabbitmq.com [12] txamqp - https://launchpad.net/txamqp [13] Esper - http://esper.codehaus.org [14] libcloud knjižnica - http://www.libcloud.org [15] PagerDuty spletna stran - http://www.pagerduty.com [16] Cloudkick Viz - https://www.cloudkick.com/viz/demo [17] Cast projekt - https://github.com/cloudkick/cast [18] Lua programski jezik - http://www.lua.org [19] Redis podatkovna baza - http://www.redis.io [20] Redis podatkovna baza (opis podatkovnih struktur) - http://www.redis.io/topics/datatypes-intro [21] Redis podatkovna baza (opis ukazov) - http://www.redis.io/commands [22] lunit Lua knjižnica za testiranje - http://www.nessie.de/mroth/lunit 27 / 37
[23] NodeJS ogrodje - http://nodejs.org [24] JavaScript The Good Parts knjiga - http://oreilly.com/catalog/9780596517748 28 / 37
9 PRILOGE 9.1 Priloga 1: Izvorna koda vtičnika -- -- Copyright (c) 2010, Cloudkick, Inc. -- All right reserved. -- module(..., package.seeall); local socket = require 'socket' local helen = require 'helen' local util = require 'util' local Check = util.check local metric_types = { } uptime_in_seconds = Check.enum.uint66, connected_clients = Check.enum.uint64, connected_slaves = Check.enum.uint64, blocked_clients = Check.enum.uint64, used_memory = Check.enum.uint64, -- in bytes bgsave_in_progress = Check.enum.uint64, changes_since_last_save = Check.enum.uint64, bgrewriteaof_in_progress = Check.enum.uint64, total_connections_received = Check.enum.gauge, total_commands_processed = Check.enum.gauge, expired_key = Check.enum.gauge, pubsub_channels = Check.enum.uint64, pubsub_patterns = Check.enum.uint64 local function get_stats(rcheck, host, port, password) local s, status, partial local ip, _ = socket.dns.toip(host) if not ip then 29 / 37
rcheck:set_error('failed resolving hostname to IP address') return rcheck local func = socket.protect(function() local sock, err = socket.connect(ip, port) local try = socket.newtry(function() sock:close() ) if not sock then rcheck:set_error('unable to connect to the redis server') return rcheck sock:settimeout(5) if password then try(sock:s('auth '.. password.. '\n')) s, status, partial = sock:receive('*a') if not partial then error() if not string.find(partial:lower(), 'ok') then rcheck:set_error('could not authenticate. Invalid password?') return rcheck try(sock:s('info\n')) s, status, partial = sock:receive('*a') if not s and not partial then error() if string.find(partial:lower(), 'operation not permitted') then rcheck:set_error('could not authenticate. Missing password?') 30 / 37
for _, line in ipairs(util.split(partial, '[^\n]+')) do repeat local split = util.split(line, '[^:]+') if not split then break local metric, value = split[1], split[2] if not (util.table.contains(metric_types, metric, 'key')) then break rcheck:add_metric(metric, metric_types[metric], tonumber(value)) until true sock:close() ) local rv = pcall(func) if not rv then rcheck:set_error('failed parsing server response') return rcheck function run(rcheck, args) local hostname, port local password = nil if not args.host then host = '127.0.0.1' else 31 / 37
host = args.host[1] if not args.port then port = 6379 else port = tonumber(args.port[1]) if args.password then password = args.password[1] rcheck = get_stats(rcheck, host, port, password) return rcheck 32 / 37
9.2 Priloga 2 izvorna koda modula s pomožnimi funkcijami za testiranje -- -- Copyright 2010, Cloudkick, Inc. -- -- 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. -- module(..., package.seeall); local lanes = require 'lanes' local util = require 'util' local buffer = alien.buffer() local getcwd = alien.default.getcwd getcwd:types('pointer', 'string') getcwd(buffer) local cwd = tostring(buffer) local local_plugins_path_default = cwd.. '/tests/' local munin_plugins_path_default = cwd.. '/tests/' local function get_conf_object(local_plugins_path, munin_plugins_path) local local_plugins_path = local_plugins_path or local_plugins_path_default local munin_plugins_path = munin_plugins_path or munin_plugins_path_default 33 / 37
local conf = {} conf.local_plugins_path = local_plugins_path conf.munin_plugins_path = munin_plugins_path return conf function get_check_module(check_name, local_plugins_path, munin_plugins_path) local rv, check = pcall(require, check_name) if rv then check.conf = get_conf_object(local_plugins_path, munin_plugins_path) return rv, check local function _run_test_tcp_server(ip, port, timeout, mode, mappings) local ip = ip or '*' local timeout = timeout or.01 local mode = mode or 'line' local mappings = mappings or {} local func = lanes.gen({ cancelstep = true}, function() local client, line, err, command, response socket = require 'socket' local server = assert(socket.bind(ip, port)) local parse_line = function(line, client) line = util.trim(line) if util.table.contains(mappings, line, 'key') then response = mappings[line] client:s(response) 34 / 37
return true return false while true do local client = server:accept() local buffer = '' local match = false client:settimeout(timeout) line, err = client:receive('*l') while line do if mode == 'line' then if not err then if parse_line(line, client) then -- Match found, close the connection client:close() else client:close() buffer = buffer.. line.. '\n' line, err = client:receive('*l') if mode == 'line' then client:close() elseif mode == 'http' then local buffer_lower = buffer:lower() for key,value in pairs(mappings) do local match_pattern = value['match_pattern']:lower() 35 / 37
'\n'.. if string.find(buffer_lower, match_pattern) ~= nil then match = true local response = value['response'] local response_len = string.len(response) local header = 'HTTP/1.1 '.. value['status_line'].. 'Content-type: '.. value['content_type'].. '\n'.. '\n\n' 'Content-Length: '.. response_len.. client:s(header.. response) break if not match then client:s('http/1.1 404 Not found\n\n') ) client:close() local lane = func() -- Wait until the lane is running while lane.status ~= 'running' do return lane function run_test_http_server(ip, port, timeout, routes) local content 36 / 37
for key,value in pairs(routes) do if value['file'] ~= nil then local path = cwd.. '/tests/'.. value['file'] local file = assert(io.open(path, 'r')) content = file:read('*a') file:close() else content = value['response'] routes[key] = { ['match_pattern'] = value['method']:upper().. ' '.. key, ['status_line'] = value['status_line'], ['content_type'] = value['content_type'], ['response'] = content } return _run_test_tcp_server(ip, port, timeout, 'http', routes) function run_test_tcp_server(ip, port, timeout, command_mappings) return _run_test_tcp_server(ip, port, timeout, 'line', command_mappings) function kill_test_server(lane, timeout) local timeout = timeout or 0 lane:cancel(timeout, true) 37 / 37