Newsgroup: it.comp.os.linux.sys
Titolo: hash in ruby

la domanda può sembrare idiota - e magari lo è veramente :) - ma gli hash in 
ruby sono i dizionari python, quindi non riesco ad affinare bene la ricerca 
google. 
Io voglio creare un hash sum, che sia un numero compreso tra X e Y, partendo 
da una determinata stringa. 
Esempio pratico, Puppet ha la funzione fqdn_rand che genera un numero compreso 
tra X e Y, associato a sua volta al nome dell'host. 
In questo caso però non posso usare questa funzione, perché voglio un numero 
che sia uguale tra gli host di uno stesso cluster.

 che sia uguale tra gli host di uno stesso cluster.
 
fin qui ci siamo: 
require 'digest/md5'
Digest::MD5.hexdigest('miastringa').hex
 
e il resto che mi manca. E mi server un one-line statement, non un programma. 

 numero che sia uguale tra gli host di uno stesso cluster.
 
Le funzioni di hash più comuni (come md5, sha1, sha256, ecc.) cercano di 
ridurre al minimo le collisioni, quindi generano valori di hash molto 
grandi.
 
Se vuoi un numero tra 1 e 1000, per esempio, ti devi scrivere tu la 
funzione (e aspettarti molte collisioni). Se vuoi un numero a 32bit (da 1 
a 4miliardi e rotti) fai l'md5 (o l'sha1 o...), prendi i primi 4 byte e 
convertili da hex a dec (ma aspettati collisioni, anche se rare)
 
Altrimenti usa l'hash value calcolato come stringa, come fa git, e amen.
 
Bye.


La collisione c'è ovviamente, ma nel caso spcifico non mi interessa per 
niente: si tratta di ottenere la key di un hash (stavolta hash nel senso 
di array...) e premetto che qualcosa ho già combinato (vedi sotto). Non so 
ancora se funziona... ma ho preso pezzi in giro e li ho messi insieme. 
 
A me serve un numero da zero a 3. In futuro se l'ambiente cresce potrebbe 
servirmi un numero da zero a 6 :) 
 
In sostanza, ho un paio di cluster Galera MariaDB/Percona, e diverse 
applicazioni che si collegano in round-robin tramite haproxy, ma il round-
robin non è sicuro per qualsiasi applicazione (per via di un problema di 
locking delle righe di mysql a livello di cluster). 
In questo caso io, tramite puppet, chiamo la mia classe, con il parametro:
"haproxy_roundrobin => false"
Avendo diverse applicazioni che si devono connettere al DB, vorrei evitare 
che tutte le applicazioni vadano ad insistere sullo stesso nodo. Allo 
stesso modo, vorrei anche evitare che le persone debbano andare a 
verificare quale nodo è più libero. 
 
Al momento, nel template uso una cosa del genere:
<% if myhost == @nodes.sort[0] -%>
 
Ma in futuro, appena verifico lo script, mi serve un numero che: 
1) ad ogni giro di puppet non cambi. 
2) sia uguale su ciascun nodo per quella specifica applicazione (come 
avevo già detto, puppet ha una funzione specifica che ti crea un numero 
associato al nome host, ma in questo caso non posso usarlo). 
 
 
Script: 
require 'digest/md5'
 
# Generate a hash-sum from a given string between 0 and a given max-value
#  - 1st arg is the string
#  - 2nd arg is the max value
module Puppet::Parser::Functions
  newfunction(:string_rand, :type => :rvalue) do |args|
 
    if args.length != 2
      raise Puppet::ParseError, "string_rand(): wrong number of args (#
{args.length}; 2 expected)"
    end
 
    basestring = args[0]
    maxvalue = args[1]
 
    unless maxvalue.is_a?(Numeric)
      raise Puppet::ParseError, 'string_rand(): second arg must be a 
Numeric'
    end
 
    Digest::MD5.hexdigest(basestring).hex % maxvalue
  end
end
 
 
 
 
-- 
Massimiliano Adamo


wahoo! questa e' la versione finale:
 
module Puppet::Parser::Functions
  newfunction(:string_rand, :type => :rvalue, :doc => <<-EOS
Generates a hash-sum from a given string between 0 and a given max-value:
- the 1st arg is the string
- the 2nd arg is the max-value and must be an integer
    EOS
) do |args|
    require 'digest/md5'
 
    if args.length != 2
      raise Puppet::ParseError, "string_rand(): wrong number of args (#{args.length}; 2 expected)"
    end
 
    basestring = args[0]
    maxvalue = args[1]
 
    Digest::MD5.hexdigest(basestring).hex % maxvalue.to_i
  end
end