ruby script to calculate primes

I have a friend who is really into Pi. In fact he claims to have the first 200 digits memorized.Me I think primes are way cooler, first of all there are a lot of them, and they occur more than you think.

For instance 2,000,000,000,003 is a prime number!

Anyway below is a script I wrote which can be used to determine is a number is a prime or you can just run it and it will start printing out all of the primes starting with the number 2.

ruby primes.rb -h  Usage: primes.rb [ -c ] or [ -o integer]  -c                               calculate primes  -o, --one_integer integer        check if one integer is a prime  -h                               Display this screen  example: primes -c calculate all primes starting with the number 2  example: primes -o check if a given integer is a prime

The code:

require 'optparse'  require 'rubygems'  ###########  # methods #  ###########  def primes(start)    foo = 2    out = Array.new    root = Math.sqrt(start)    div = root.to_i    while  foo <=  div      foo = foo + 1      ans = start.to_f / foo.to_f      nu,de = ans.to_s.split('.')      if de == "0"        out.push(de)      end    end  return out  end  ##################  # define options #  ##################  options = {}  optparse = OptionParser.new do|opts|     opts.banner = "Usage: primes.rb [ -c ] or [ -o integer]"     options[:calculate_primes] = false     opts.on( '-c', 'calculate primes' ) do        options[:calculate_primes] = true     end    options[:one_integer] = nil      opts.on( '-o integer', '--one_integer integer', "check if one integer       is a prime" ) do |check_this|        options[:one_integer] = check_this     end     opts.on( '-h', 'Display this screen' ) do        puts opts        puts "example: primes -c calculate all primes starting with the number 2"        puts "example: primes -o check if a given integer is a prime"        exit     end   optlength = ARGV.length     if optlength < 1        puts opts.banner        exit     end  end  optparse.parse!  if options[:calculate_primes]    start = 2    while start > 1      start = start +1      if start.odd?       out = primes(start)       count = out.count         if count == 0           puts start.to_s         end      end    end  end  if options[:one_integer]    check_this = "#{options[:one_integer]}"    if check_this.to_i.even?      puts check_this.to_s + " is not a prime number"      exit    end    out = primes(check_this)      count = out.count      if count == 0        puts check_this.to_s + " is a prime number"      else        puts check_this.to_s + " is not a prime number"      end  end

Update:Turns out Ruby has a built in function to do this in 3 lines.

require 'mathn'  list_primes = Prime.new  puts list_primes.each { |prime| print prime.to_s + "n", " "; break unless prime > 1 }

Thank you mathn!

dynamic preseed file for ubuntu using sinatra

To build ubuntu physical ubuntu servers we use ubuntu preseed.

This works great but if you use a static preseed file you end up building a host that doesn’t have its hostname or static ip address set. This means that you have to manually set it afterward and we decided to automate it.

BTW it took us a while to figure out how to set a static ip in a preseed file.We blogged about it here:network-preseeding-debianubuntu-with-a-static

To do this we wrote a small sinatra app that dynamically generates the preseed file with the hostname and static ip address.

This is done by looking up the mac address of the requested host from the arp table and comparing it to a pipe delimited file that contains the mac address, what the static ip should be and its hostname.

The list is stored in a file named ip2mac.txt and was populated by a script.

The ip2mac.txt file looks like this:

172.28.0.71|a4:ba:db:35:e6:09|chi-devops11a  172.28.0.72|78:2b:cb:03:c5:44|chi-devops11b

Instead of calling a static preseed file from the pxelinux.cfg/default file we instead make a request to the sinatra app which generates it dynamically.The line in the default file we use looks like this:

append console=tty0 console=ttyS1,115200n8 initrd=ubuntu-10.04-server-amd64-  initrd.gz auto=true priority=critical preseed/url=http://172.27.0.115:4567/lucid-preseed-noraid interface=eth0 netcfg/dhcp_timeout=60 console-setup/ask_detect=false console-setup/layoutcode=us console-keymaps-at/keymap=us locale=en_US --

When the request is made the sinatra app does the following:

*  1. looks up the mac address of the request from the apr table*  2. compares the mac address to the matching line in ip2mac.txt*  3. uses the ip and hostname to populate hostname and ip variables in the preseed file*  4. returns the preseed file to the host making the request

The code:

require 'rubygems' # skip this line in Ruby 1.9  require 'sinatra'  require "erb"  require 'logger'  def log(message)    flog = Logger.new('foo.log')    flog.info(message)  end  def lookup_mac(mac)    rr = Array.new    hostfile = File.open("ip2mac.txt","r")    hostfile.readline    hostfile.each do |line|      list_ip,list_mac,name = line.split('|')      if mac.match(list_mac)    rr.push(list_ip)      end    end  return rr[0]  end  def get_mac_address()    ip =  @env['REMOTE_ADDR']    cmd = "arp -n " + ip.chomp + " | grep -v Address | awk '{print $3}'"    mac  = `#{cmd}`   return mac  end  def rev_lookup(ip)    cmd = "host " + ip + " | awk '{print $5}'"    hostname = `#{cmd}`fqdn = hostname.chop.chopreturn fqdn  end  get '/lucid-preseed-noraid' do    mac = get_mac_address()    log(mac)    ips = lookup_mac(mac)    log(ips)    fqdns = rev_lookup(ips)    @ip = ips    @fqdn = fqdns    log(fqdns)    erb :lucid_preseed_noraid  end  get '/lucid-preseed-nosrv' do    mac = get_mac_address()    log(mac)    ips = lookup_mac(mac)    log(ips)    fqdns = rev_lookup(ips)    @ip = ips    @fqdn = fqdns    log(fqdns)    erb :lucid_preseed_nosrv  end  get '/' do    "ops11"  end

To start the sinatra app just run the following:

ruby preseeder.rb

access nested node attributes in a chef recipe

If you use chef bookmark this page as you will need to access nested keys at some point.Chef uses ohai to build a hash of each host chef-client is installed on.In a recipe this is stored in a hash named node.If you want to access a value for a key its simple.

ip = node['ipaddress']

Also if you want to determine if a key exists before you try and access it you can use attribute?()

node.attribute?('ipaddress')

BTW in most cases you will want to make sure the key exists because if it doesn’t chef-client will throw an error.

For nested keys its a bit more difficult, first of all you should always make sure the keys exists. This involves using the has_key? method in nested if statements.Then you can just pull the value from the keys.Below is one way to do it in a recipe.In this example I am making sure the keys filesystem>/dev/sda6>mount exist in the node hash.Then once I’m sure the hash exists I pull out the value.

if node.has_key? "filesystem"     if node["filesystem"].has_key? "/dev/sda6"        if node["filesystem"]["/dev/sda6"].has_key? "mount"           if node['filesystem']['/dev/sda6']['mount'] == '/srv'                execute "foo" do                command "touch /tmp/nested_keys_exist!"                action :run              end           end       end     end  end

riak cluster backup script with compression #riak

This script will create a compressed back up of a riak cluster and keep the previous days copy.I still have to add restoring as an option.

#!/usr/bin/env ruby  t = Time.new  f = t -86400  today = t.strftime("%Y-%m-%d")  yesterday = f.strftime("%Y-%m-%d")  def delete_old()    unless Dir.glob('/net/fs11/srv/posterous/nfs/riak/*-old').empty?      l = Dir.glob('/net/fs11/srv/posterous/nfs/riak/*-old')      puts "deleting oldeest backup"      File.delete(l[0])    end  end  def rotate_last(yesterday)    f = "/net/fs11/srv/posterous/nfs/riak/riak_backup-" + yesterday.chomp  + ".bz2"    t = f + "-old"    if  File.exists?(f)      puts "rotating old backup to old"      File.rename(f,t)    end  end  def run_backup(today)    puts "creating backup file"    dump = "/usr/sbin/riak-admin backup riak@172.27.0.113 riak       /net/fs11/srv/posterous/nfs/riak/riak_backup-" + today.chomp + " all"    `#{dump}`  end  def compress(today)    puts "compressing backup"    compress = "/usr/bin/pbzip2 /net/fs11/srv/posterous/nfs/riak/riak_backup-" + today.chomp    `#{compress}`  end  delete_old()  rotate_last(yesterday)  run_backup(today)  compress(today)

Managing different environments with chef attributes and a case statement.

Like everyone else using chef I manage several environments such as dev, staging, testing & production. Many times the only difference is a single config file( for me that is most daemons such as mongo, mysql, redis, varnish, etc….)There are several ways of doing it. One way is to use templates( which I find to be confusing and more than what I need).Another way is to create a different recipe for each environment ( mongo.dev, mongo.prod ,etc….) This seams like too much work.What I found to be the simplest and cleanest way is to use the case statement with attributes.Don’t forget the creators of chef( opscode ) allow you to exit the DML and just write ruby so take advantage of it.
Lets so you have a recipe to install and configure mongo.The entire recipe is the same except for the config file.This can be easily managed using attributes and case statement.First create an empty role for each of the different environments (dev, prod, etc…), then assign them the same attribute in this case app_environment with a different value for the environment.In json this looks like this:

{"overrides":{},"defaults":{"app_environment":"dev"}}

Then in the recipe use the case statement to determine which config file the node gets:

cookbook_file "/etc/mysql/my.cnf" dosource_file = case node[:app_environment]when "dev" then "my.cnf.dev"when "prod" then "my.cnf.prod"endsource source_filenotifies :restart, "service[mysql]", :immediately mode "0644"owner "root"end

That does it I’m switching to ruby

Every sysadmin needs a scripting language that goes beyond bash.
True bash is a main stay and needs to be in everyone’s tool box but for more feature rich scripting you should have a higher level language available to you.
When I started working in this field my choices were python or perl. I decided on perl because many tools that sysadmin’s use are written in perl (nagios), also there was a perl course available at my local night school and not for python.
10 years later I figured its time to learn a new language for a couple of reasons.
1.    Many new sysadmin tools are being written languages other than perl
2.    I’m bored of perl
3.    Want to challenge myself

So what language should I learn?
I decided on the 3 languages because their use is wide spread, well supported by the community and all have frameworks for web based apps.
Java, Ruby & Python.
I began by doing the hello world for all three. Right away I noticed in java you have way more typing to do before and after your code.
Void public static main????

No thanks. That left me with python and ruby. The hello world examples were straight up so I decided to try a while loop. I wanted my while loop to print “Hello world” ten times.  This didn’t work for me in python; I needed to end the while line with a “:”, but with ruby it just worked!!!

start = 1
finish = 10
while start <= finish
puts  “hello world”
start  = start + 1
end

Amazing, a while loop with no syntax at all, it just works like the way you think it would.