$ cat /dev/random
ripley's miscellanous debris

snippet Tag Archive

Ruby, XMLRPC e Trac

(2006-06-13)

Io e i miei compari usiamo coordinare e tracciare le nostre attivita’, idee e progetti mediante un software chiamato Trac.

Trac e’ l’insieme di un wiki, un vcs browser e un tracker.

Fornisce quindi alcuni degli strumenti fondamentali per coordinare dei progetti di sviluppo, integrando il tutto in un unico software.

Lo utilizziamo oramai da quasi un anno e’ tranne qualche piccola pecca (l’editing contemporaneo delle pagine wiki… mi sa che se non patch-ano loro ci mettero’ presto le mani io :-P ), ci troviamo benissimo :-)

La componente vcs browser inizialmente era stata pensata semplicemente come un svn browser, il che si e’ rivelato limitante visto il proliferare di varie alternative che prendono sempre piu’ piede (tra cui bzr, git, darcs, mercurial etc.) per cui presto sono venuti fuori dei fork per supportare questo o quel sistemi di revisione.

In particolare noi usavamo il branch sperimentale con il supporto per darcs e bzr (sviluppato tra l’altro da alcuni italiani http://progetti.arstecnica.it/trac+darcs) , il quale funziona piu’ che bene…

ma… c’e’ ovviamente un ma…

una limitazione rispetto al classico utilizzo di Trac+svn e’ rimasta :-(

L'impossibilita' di chiudere automaticamente un bug mediante il semplice commit.

E si… e questo e’ dovuto semplicemente al fatto che subversion esegue gli hook sull’host in cui risiede il repository e in cui e’ plausibile ci sia anche il trac.

Questo non vale invece per bzr (ne per gli altri sistemi di revisione decentralizzati, che prevedono lato repository un comune accesso sftp o equivalente)... in questo caso gli hook vengono richiamati lato client, per cui lo script previsto per subversion non e’ idoneo per bzr :-(

Quindi?

Quindi dobbiamo far in modo di poter chiudere dei ticket da remoto… il modo migliore per farlo sarebbe sicuramente qualcosa tipo XMLRPC.

Su TracHacks troviamo quello che fa al caso nostro:

un bel plugin XMLRPC per Trac :-)

Unico inconveniente… il plugin e’ stato sviluppato sulla versione 0.10 di Trac, cioe’ la versione in sviluppo, per la quale non esiste un branch bzr… ma esiste un plugin per il nuovo strato di virtualizzione della componente vcs di Trac.

Installare Trac 0.10

Trac viene sviluppato utilizzando svn come sistema di revisione, quindi per ottenere l’ultima release di sviluppo:

svn co http://svn.edgewall.com/repos/trac/trunk trac

Per compilare i sorgenti ed installarli nel sistema:

$ cd trac
$ sudo python setup.py install --prefix=/usr/local/

Compilazione dei plugin trac

Trac utilizza per i plugin una tecnologia molto simile ai jar di java, i python egg.

Per generare l’egg di un plugin:

$ python setup.py bdist_egg

Per installare il plugin e’ sufficiente copiare il file .egg ottenuto, contenuto nella directory dist, nella directory plugin dell’istanza trac in cui vogliamo installarlo, ed in alcuni casi aggiungere alcuni parametri in config/trac.ini.

XMLRPC Trac Plugin

Il plugin XMLRPC di trac (compatibile con la versione 0.10 in via di sviluppo) si trova all’indirizzo:

Per installarlo e’ sufficiente generare e copiare l’egg file nell’istanza di trac interessata e modificarne il trac.ini aggiungendo la linea:

[components]
tracrpc.* = enabled

Bzr Trac Plugin

Il plugin XMLRPC di trac (anch’esso compatibile con la versione 0.10) si trova all’indirizzo:

E ad essere precisi il repository (bzr ovviamente) del plugin, che non e’ ancora stato rilasciato ufficialmente:

potere prelevarlo mediante bzr:

  bzr get http://samba.org/~jelmer/bzr/trac-bzr

Analogamente a quanto fatto per il plugin XMLRPC, generiamo l’ egg file e lo copiamo nella directory plugins dell’istanza trac che ci interessa, e aggiungiamo in config/trac.ini le seguenti righe:

[components]
trac-bzr.* = enabled
[trac]
repository_type = bzr
repository_dir = /srv/repos/alcadps/trunk

Primi XMLRPC-passi

Come primo passo verifichiamo che il plugin xmlrpc sia attivo visitando l’indirizzo http://miohost.miodominio/tracname/xmlrpc.

Se il plugin sta funzionando correttamente dovremmo vedere una pagina contenente una descrizione di tutte le funzioni che il plugin XMLRPC ci mette a disposizione.

A questo punto possiamo provare un primo semplice script in ruby…

XMLRPC-Client in ruby

Il primo snippet ha la semplice funzione di elencare le funzioni messe a disposizione da trac:

require "xmlrpc/client"

server = XMLRPC::Client.new("localhost", "/alcadps/xmlrpc", 3000)

system = server.proxy('system')
puts system.listMethods

Il secondo snippet richiede lo stato di un ticket:

require "xmlrpc/client"

server = XMLRPC::Client.new("localhost", "/alcadps/xmlrpc", 3000)

ticket = server.proxy('ticket')
puts ticket.get('9')[3]['status']

La funzione ticket.get ritorna un array in cui il 4 elemento e’ un hash contenente i dettagli del ticket.

E’ importante notare comunque che i due snippet precedenti accedono a trac in modo anonimo, di conseguenza i permessi saranno quelli dell’utente anonymous.

Per avere accesso a tutte le funzionalita’ messe a disposizione ad utenti con permessi aggiuntivi e’ necessario autenticarsi.

Purtroppo questo ha messo in luce un problema dovuto alla tecnica di autenticazione di trac.

Problema di autenticazione su XMLRPC

Il problema e’ dovuto al redirect (Net::HTTPSeeOther) che genera trac dopo aver autenticato un client. In XMLRPC cio’ non ha molto senso e di conseguenza sia l’implementazione python che ruby di xmlrpc non contempla questa risposta da parte del server.

Sono riuscito a bypassare il problema con qualche piccolo hack sulla classe XMLRPC::Client .

Questo hack comunque non credo sia la soluzione migliore…

HTTP See Other Workaround

Il workaround piu’ elegante che mi e’ venuto in mente e’ stato quello di autenticarmi utilizzando direttamente il package Net::HTTP (il quale e’ utilizzato anche dalla classe XMLRPC::Client ), estrarre il cookie dalla risposta del server, e utilizzarlo nell’oggetto XMLRPC::Client creato per comunicare con il server… e per questo dobbiamo ringraziare i lungimirandi sviluppatori di XMLRPC::Client che hanno previsto l’accessor cookie in lettura e scrittura :-)

require "xmlrpc/client"
require 'net/http'

header = {
  "User-Agent"     => "ruby Net::HTTP",
  "Content-Type"   => "text/xml; charset=utf-8",
}

Net::HTTP.start('yourhost.yourdomain',3000) {|http|
   req = Net::HTTP::Post.new('/yourtrac/login/xmlrpc')
   req.basic_auth 'your_login', 'your_password'
   response = http.request(req,header)

   @cookie = response['Set-Cookie']
}


# Make an object to represent the XML-RPC server.
server = XMLRPC::Client.new("yourhost.yourdomain", "/yourtrac/xmlrpc", 3000)

server.cookie = @cookie

system = server.proxy('system')
ticket = server.proxy('ticket')
tstatus = server.proxy('ticket.status')

ticket.update(9, 'closed by xml-rpl :-)', { 'status' => 'closed' })

Volendo si puo’ incapsulare la funzionalita’ di autenticazione in una bella funzioncina e riutilizzare il concetto in tutti gli script di automation per trac che potremo sviluppare :-P

Happy Hacking


Flickr API e Appello ai coders :-)

(2006-06-02)

Sei un programmatore?

Ti fischiano spesso le orecchie ed il tuo otorinolaringoiatria non sa darsi una spiegazione?

Potresti non aver commentato a sufficienza il tuo codice :-) Qualche altro programmatore dall’altra parte del mondo potrebbe essere li davanti al tuo codice pensando:

“haahhaksgjmfjjiirkmskb!!!”

Che non e’ un insulto… ce lo dice Irman… deriva dal greco e significa:

saggezza

Ma come la conoscenza, che senza la saggezza e il senso del sacro e’ fina a se stessa, cosi’ il nostro codice sara’ inutilizzabile da parte di molti sviluppatori se non viene commentato a sufficienza :-(

Lo so... e' difficile da digerire... mica posso fare tutto io...
... perche' non si commenta lui da solo...
ma lo sai a che ora mi sono alzato?... alle 7 meno un quarto...

Anche io ogni giorno penso: “uhm… forse un po’ di commenti ed un getting started per rog ci vorrebbero proprio”

Ma poi nun me coddhra... preferisco scrivere altro codice :-P

Ma bisogna resistere ai nostri istinti animaleschi :-)

Comunque, a parte gli scherzi, oggi ho provato per gioco un binding ruby per l’API di flickr e anche se flickr non mi e’ molto simpatico come servizio ho deciso di mettere online l’esperimento (andato a buon fine) visto che l’api che ho usato (rflickr) e’ piuttosto carente di documentazione e il GETTING_STARTED e gli esempi non sono sufficienti a permetterti di avere uno snippet funzionante senza aver prima lanciato un buon numero di:

“hahhaksgjmfjjiirkmskb!!!”

ma andiamo avanti…

Il magico mondo di Flickr

Il magico mondo di Flickr e’ un quartiere del magico mondo yahoo... per poter utilizzare il servizio quindi dovrete avere un magico account yahoo... che significa rispondere ad una serie domande del tipo:

uff… che palle… mi sta quasi passando la voglia…

ora che avere il vostro magico account yahoo potete attivare il vostro magico account flickr... e quindi dovrete rispondere ad altre fantastiche domande:

Ah… ora possiamo upload-are le immagini… aspetta ma noi lo vogliamo fare con l’API… benissimo devi attivare una application key... altre domande :-( ...

coff… coff… ammazza oh quante ne vogliono sapere… c’e’ l’abbiamo fatta: abbiamo la nostra application key e shared secret

E mo? che ce faccio? dopo tutta sta fatica DEVE funzionare.

rflickr

rflickr e’ un binding ruby per comunicare con il webservice flickr, non so se e’ l’unico ma l’ho provato solo per capire insieme ad un amico perche’ non voleva funzionare, quindi non importa.

Potete installarlo con un semplice:

gem install rflickr -r

e vederne il contenuto con:

gem contents rflickr

La documentazione e’ composta da un GETTING_STARTED e alcuni esempi.

Se proverete ad usare gli esempi cosi’ come sono…

non funzioneranno

Si si provate… ma tanto non funzionano :-)

In effetti il GETTING_STARTED un suggerimento ce lo da in proposito… ma comunque non e’ che ti aiuti piu’ di tanto :-P

Esperimento

Per il test ho scelto un esempio a caso (quello che mi sembrava piu’ semplice) e l’ho provato: album_test.rb

In questo esempio ci sono 2 problemi che ci mandano fuori strada:

  1. non vengono passate l’application key e lo shared secret al costruttore di Flickr (per cui non riusciremo ad autenticarci)
  2. le applicazioni hanno necessita’ di autorizzazione esplicita da parte dell’utente per poter accedere ai dati (autorizzazione che viene accordata visitando un url che ci viene comunicato durante la fare di autorizzazione)

Questo si traduce in una piccolissima (quanto fondamentale) modifica al codice dell’esempio:

token_cache_file = "./token.cache" # path del file cache del token

flickr = Flickr.new(token_cache_file,APP_KEY,SHARED_SECRET)
  unless flickr.auth.token
    flickr.auth.getFrob
    url = flickr.auth.login_link
    puts "You must visit #{url} to authorize this application. "+ 
     "Press enter when you have done so. This is the only time "+
     "you will have to do this."
    gets
    flickr.auth.getToken
    flickr.auth.cache_token
end

da sostituire all’originale (ed eccessivamente ottimista :-)):

flickr = Flickr.new('MY_TOKEN')

A questo punto loggatevi sul vostro magico account flickr, upload-ate qualche immagine/foto e organizzatele in un Set (chiamato ad es. “prova”).

Siete pronti a provare l’esempio:

ruby album_test.rb prova

visitate l’url suggerito e date l’ok per l’accesso dello script al vostro account e premete invio (nella console in cui avete lanciato l’esempio :-)

Conclusioni

Questi nuovi servizi community-centralizzata-commercializzata non mi piacciono un granche’... sono servizi di community ma:

Li rende troppo suscettibili a manipolazioni... cambi di servizio o di politica dell’azienda... uhm… non la vedo come una cosa proprio comunitaria.

Inoltre nel caso specifico di Flickr, mi sembra tutto sommato un servizio che non nasconde una poi cosi’ elevata complessita’ da rendere difficile la realizzazione di alternative libere.

Cosi’ su due piedi mi sembra un applicazione rails da 2 soldi (almeno l’idea di base) e chiunque potrebbe avere il suo servizio personale sul proprio sito web o comunitario sul proprio sito web comunitario.

Troppo estremista? ai posteri l’ardua sentenza.

buon divertimento con il vostro magico account flickr.


Net::Telnet ti da una mano

(2006-05-27)

Ruby e’ un ottimo linguaggio ad oggetti e come molti sapranno sta letteralmente spopolando nell’ambito delle web-application, mettendo in serie difficolta’ php (che paga ora la sua scarsa innovativita’, preferendo preservare lo status quo), java (e questo pochi se lo aspettavano) e python (che invece in ambito web e’ entrato di fresco), ma pochi ancora lo utilizzano in ambito di scripting di sistema.

Durante l’amministrazione di sistema si presentano spesso delle situazioni che richiedono l’esecuzione di azioni ad elevata meccanicita’.

Queste azioni meccaniche oltre ad essere molto noiose (scazzac…zi e’ il termine tecnico utilizzato da noi del settore :-)) e fonte di enormi perdite di tempo, sono soprattutto molto error prone.

La mente umana si sa e’ dotata di una innata fantasia ma e’ molto scarsa in tutti i lavori ripetitivi:

10 ripeti un milione di volte la stessa cosa 
20 per fare prima o poi qualche immane c..zata 
30 di cui ti pentirai nelle seguenti 12 ore... 
30 goto 10

Quindi abbiamo sufficienti scuse per cercare di scappottarci (termine tecnico che sta per riassegnare le risorse) anche questa ingrata mansione e sbolognarla alla nostra protesi al cervello (il termine tecnico usato per indicare il pc).

Divagazione Storica

Inizialmente gli amministratori di sistema automatizzavano tutto utilizzando le tante micro-utility di unix e lo shell scripting come collante.

Ma man mano che i compiti si fanno complessi (e quindi lo script piu’ lungo e articolato) gli script shell cominciano a diventare piuttosto difficili da comprendere e debuggare e si comincia il freeclimbing sugli specchi…

E cosi’ nasce perl… con il quale anche gli script piu’ piccoli risultano piuttosto difficili da comprendere e debbuggare :-PPPPP

A parte gli scherzi perl ha rappresentato un grosso passo avanti per quanto riguarda la complessita’ dei compiti che gli script di sistema sono in grado di compiere, con la sua straordinaria capacita’ di sintesi, ma questo purtroppo a discapito della leggibilita’ :-( ... ora il compito preferito da noi amministratori e’ capire cosa ca..o fanno questi script in perl che nemmeno Larry Wall riuscirebbe a leggere :-)))

Ruby da parte sua:

A mio avviso e’ un ottimo candidato per segnare una vera svolta per quanto riguarda la qualita’ del codice di questi script di sistema, e’ tutto potenziale che aspetta solo la carica per poter esplodere.

In ambito web la carica e’ stata Ruby On Rails, in questo ambito chissa’ :-)

Nel mentre siamo qui ad aspettare questa carica possiamo saggiarne un piccolo esempio concreto.

Scenario

Ho un piccolo router adsl (un USR 9105) che, anche se non evidenziato da nessuna parte sulla scatola, e’ equipaggiato con un kernel linux e un piccolo sistema composto da busybox, iptables, route etc. etc.

Il router puo’ essere configurato attraverso una comoda (quanto limitata) interfaccia web e attraverso un’interfaccia telnet.

Attraverso l’interfaccia telnet abbiamo a disposizione una shell busybox ristretta (non c’e’ ls ad esempio… ma c’e’ cat :-P) e un po’ di comandi tra cui oltre a quelli specifici dell’usr9105 abbiamo piu’ o meno ufficiosamente iptables :-)

Problema

L’interfaccia telnet e’ stata una manna perche’ mi permette di bypassare alcune limitazioni dell’interfaccia web (e non del router in se) come ad esempio la possibilita’ di NATting di una sola classe di indirizzi:

cioe' se uso 2 classi di indirizzi (ad esempio 192.168.0.x e 10.0.x.x) 
posso assegnare due ip al router (uno per classe) ma una sola potra' 
essere NATtata (quella dell'indirizzo primario)

CHE PALLE!!!

Per fortuna abbiamo l’interfaccia telnet e iptables!!!

Ci logghiamo via telnet, facciamo un bel:

iptables -t nat -A POSTROUTING --src 10.0.0.0/16 -j MASQUERADE

e abbiamo risolto.

Si… ma fino a quando non si riavvia il router!!!

CHE PALLEEEEE!!!

Non ci vuole molto a rendersi conto che in questo modo non stiamo facendo altro che caricarci un compito ingrato e ripetitivo (alla Lino Banfi operaio in fabbrica) che si presentera’ quando meno lo vorremo.

Soluzione

Armati di Ruby e Net::Telnet possiamo risolvere in 4 e 4 = 8 (righe di codice ;-)):

require 'net/telnet'

def test_172nat(usr9105)
  nat_table_buf = ""
  test_172nat_rx = /\n(MASQUERADE).*(10.0.0.0)/

  usr9105.cmd("iptables -t nat -L") { |c| nat_table_buf << c }

  if result = test_172nat_rx.match(nat_table_buf) then
    puts "OK: #{$1} #{$2}"
    return true
  else
    puts "KO"
    return false
  end
end

usr9105 = Net::Telnet::new("Host" => "10.0.0.1",
                               "Timeout" => 10,
                               "Prompt" => /[$%#>] \z/n)
usr9105.login("admin", "yourpassword") { |c| print c }

if not test_172nat(usr9105) then
   usr9105.cmd("iptables -t nat -A POSTROUTING --src 10.0.0.0/16 -j MASQUERADE")
   test_172nat(usr9105)
end

usr9105.close

Ovviamente questo e’ solo un piccolo snippet (imperfetto e senza un error handling serio), ma credo che renda l’idea.

Lo script e’ pienamente leggibile ma allo stesso tempo molto compatto, senza considerare il fatto che all’aumentare della complessita’ ci verranno in aiuto le caratteristiche di orientamento agli oggetti e meta-programmazione proprie di ruby (che sono inesistenti o primitive in perl)

Conclusione

Se siete amministratori di sistema imparate ruby e abbandonate perl al piu’ presto prima di bruciare nelle fiamme dell’inferno… ricordate

Dovete trovare ruby...
 prima che sia lui a trovare voi...

e se conoscete altri amministratori di sistema che scrivono script in perl raccontate anche a loro la parabola della povera Mildred :-)


Full of CC licensed ripley's debris
HTML and CSS Design by Nicolas Fafchamps
Generated with Rote and Rog