vagrant set ip address in loop with maths - ruby

I am trying to write a script to set up a dynamic number of nodes in vagrant.
so it runs as
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
$num_instances = 3
(1..$num_instances).each do |i|
$ip = "10.1.10.#{i}+80"
config.vm.define "node#{i}" do |node|
node.vm.box = "generic/ubuntu1604"
node.vm.provider :libvirt do |domain|
domain.memory = 1024
domain.cpus = 1
domain.nic_model_type = "virtio"
domain.kvm_hidden = true
end
node.vm.hostname = "t2-node#{i}"
node.vm.network "public_network",
:ip => $ip,
:type => "bridge" ,
:dev => "br1",
:mode => "bridge",
:use_dhcp_assigned_default_route => true
end
end
end
but I get an error:
Unknown interface eth1
Error: ??? prefix is expected rather than "10.1.10.3+80/255.255.255.0".
so I guess the addition for the $1p variable isn't being done properly?
any ideas?

This is more a ruby question than a vagrant question but you do your string interpolation wrong so the statement
$ip = "10.1.10.#{i}+80"
returns a string that is 10.1.10.3+80 and of course this is not a valid IP.
what you're looking at is
ip = "10.1.10.#{i+80}"
which will make the right math.
You can easily check in irb on your command line
irb(main):001:0> num_instances = 3
=> 3
irb(main):002:0> (1..num_instances).each do |i|
irb(main):003:1* ip = "10.1.10.#{i+80}"
irb(main):004:1> p ip
irb(main):005:1> end
"10.1.10.81"
"10.1.10.82"
"10.1.10.83"
=> 1..3
PS: Note, you dont need all the $ when you declare variables in ruby.

Related

Provisioning a vagrantfile using json

Hey so i want to provision my vagrantfile using json, i have done this with yaml before and tried to follow the syntax alongside a little research on json. I have included print commands to confirm that the values from my json are readable but im struggling in understanding how to put them into my code as values? so my question is how do i call these values within a vagrant file?
require 'json'
filej = File.read('info.json')
jinfo = JSON.parse(filej)
vagrant.configure("2") do |config|
jinfo.each do |key, value|
p key
p value
config.vm.define jinfo['name'] do |js|
js.vm.box = jinfo['box']
js.vm.network 'private_network', ip: js['ip_addr']
js.vm.provider "virtualbox" do |vb|
vb.cpus = jinfo['vcpu']
vb.memory = jinfo['ram']
end
end
end
end
and my json is as follows
[
{
"name": "jenkins",
"box": "centos/7",
"ram": 4096,
"vcpu": 2,
"ip_addr": "192.168.33.11"
},
{
"name": "server",
"box": "centos/7",
"ram": 4096,
"vcpu": 2,
"ip_addr": "192.168.33.10"
}
]
If your Vagrantfile is same as pasted above, it looks like this:
require 'json'
filej = File.read('info.json')
jinfo = JSON.parse(filej)
vagrant.configure("2") do |config|
jinfo.each do |key, value|
p key
p value
config.vm.define jinfo['name'] do |js|
js.vm.box = jinfo['box']
js.vm.network 'private_network', ip: js['ip_addr']
js.vm.provider "virtualbox" do |vb|
vb.cpus = jinfo['vcpu']
vb.memory = jinfo['ram']
end
end
end
end
jinfo is actually an array and not a hash. You are trying to loop through each key value pair of the json instead of looping through each value. Try this:
require 'json'
filej = File.read('info.json')
jinfos = JSON.parse(filej)
vagrant.configure("2") do |config|
jinfos.each do |jinfo|
p jinfo
config.vm.define jinfo['name'] do |js|
js.vm.box = jinfo['box']
js.vm.network 'private_network', ip: js['ip_addr']
js.vm.provider "virtualbox" do |vb|
vb.cpus = jinfo['vcpu']
vb.memory = jinfo['ram']
end
end
end
end
I changed the variable names slightly. You can remove the print part altogether and try.
Hope this helped.

Vagrant execute command on specific guest after other guests are up

I have the following Vagrantfile:
Vagrant.configure(VAGRANT_API_VERSION) do |config|
config.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 2
end
config.vm.define :master do |master_config|
master_config.vm.box = "centos/7"
master_config.vm.host_name = 'saltmaster.local'
master_config.vm.network "private_network", ip: "172.16.10.10"
master_config.vm.synced_folder ".", "/vagrant", disabled: true
master_config.vm.synced_folder "states", "/vagrant/states", type: "virtualbox"
master_config.vm.synced_folder "pillar", "/vagrant/pillar", type: "virtualbox"
master_config.vm.provision :salt do |salt|
salt.master_config = "etc/master"
salt.install_type = "git"
salt.install_args = "v2016.11.7"
salt.no_minion = true
salt.install_master = true
salt.verbose = true
salt.colorize = true
salt.bootstrap_options = "-P -c /tmp"
end
end
config.vm.define :minion1 do |minion_config|
minion_config.vm.box = "centos/7"
minion_config.vm.host_name = 'saltminion1.local'
minion_config.vm.network "private_network", ip: "172.16.10.11"
minion_config.vm.synced_folder ".", "/vagrant", disabled: true
minion_config.vm.provision :salt do |salt|
salt.minion_config = "etc/minion1"
salt.install_type = "git"
salt.install_args = "v2016.11.7"
salt.verbose = true
salt.colorize = true
salt.bootstrap_options = "-P -c /tmp"
end
end
config.vm.define :minion2 do |minion_config|
minion_config.vm.box = "centos/7"
minion_config.vm.host_name = 'saltminion2.local'
minion_config.vm.network "private_network", ip: "172.16.10.12"
minion_config.vm.synced_folder ".", "/vagrant", disabled: true
minion_config.vm.provision :salt do |salt|
salt.minion_config = "etc/minion2"
salt.install_type = "git"
salt.install_args = "v2016.11.7"
salt.verbose = true
salt.colorize = true
salt.bootstrap_options = "-P -c /tmp"
end
end
end
Now after all the machines are up and running, I want to execute a command on the salt master using master_config.vm.provision "shell", inline: "salt '*' state.apply".
But the problem is, once Vagrant finished provisioning minion2, it can't access the master_config machine anymore. I think there should be a way to execute that command, without having to execute vagrant ssh master -c stalt '*' state.apply. I don't want to use a command on the host. And the highstate needs to be applied AFTER all the minions are up, due to some networking related configurations and states.
Can someone help me out?
... continued from comments, making here for the example.
You can use the vagrant trigger plugin. As mentioned in the doc:
Starting from version 0.5.0, triggers can also be run as a provisioner
you can target a specific instance (see https://github.com/emyl/vagrant-triggers#options)
so the following at the end of the file should work:
config.vm.provision "trigger", :vm => "minion_config" do |trigger|
trigger.fire do
run "script"
end
end

Multi-VM Vagrantfile with shared chef settings but different roles

I'm trying to use a single Vagrantfile to define 2 VMs with different Chef roles, and I'd like to share certain chef configuration betweeen both vms, but specify the runlist, environment, and attributes seperately. Is there a way I can do this without duplicating the common settings?
For instance, if I did something like:
# Development system
config.vm.define "dev", primary: true do |dev|
dev.vm.hostname = "server1"
dev.vm.provider "virtualbox" do |v|
v.name = "server1"
end
dev.vm.provision "chef_client" do |chef|
chef.run_list = [ "role[dev-system]" ]
chef.environment = "development"
chef.json = {
"key" => "value1"
}
end
end
# Production system
config.vm.define "pro", primary: true do |pro|
pro.vm.hostname = "server2"
pro.vm.provider "virtualbox" do |v|
v.name = "server2"
end
pro.vm.provision "chef_client" do |chef|
chef.run_list = [ "role[pro-system]" ]
chef.environment = "production"
chef.json = {
"key" => "value2"
}
end
end
config.vm.provision "chef_client" do |chef|
chef.chef_server_url = "https://example.com/organizations/tsrd"
chef.validation_key_path = "~/my-validator.pem"
chef.validation_client_name = "my-validator"
chef.delete_node = true
chef.delete_client = true
# ... PLUS OTHER COMMON CONFIGURATIONS...
end
Would it apply the individual chef sections for each vm along with the shared section, or just override the first sections with the last? If not, is there an easier way to do this? Perhaps a wrapper function that is called inside each vm definition block?
For this purpose I took another way.
I define my vms as a hash in the vagrant file like this:
machinesBases = {
'srv0643' => {
'ip_address' => '172.30.0.140',
'env' => 'Z1',
'alias' => 'z1frnessus01',
'roles' => ['nessus'],
'additionnal_recipes' => ['java']
},
'srv0683' => {
'ip_address' => '172.30.0.51',
'env' => 'P1',
'alias' => 'p1frkegin01',
'roles' => [],
'additionnal_recipes' => ['kegin_cookbook']
}
}
And then I loop over it, using the hash values
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
machinesBases.each do |hostname,properties|
config.vm.define hostname do |box|
box.vm.hostname = "#{hostname}.my.domain.org"
box.vm.network :private_network, ip: properties["ip_address"]
box.vm.provision :chef_client do |chef|
chef.chef_server_url = CHEF_CHEF_SERVER_URL
chef.validation_key_path = CHEF_VALIDATION_KEY_PATH
chef.environment = properties["env"]
chef.add_recipe "base-linux"
properties['roles'].each do |role|
chef.add_role role
end
properties['additionnal_recipes'].each do |recipe|
chef.add_recipe recipe
end
end
end
end unless machinesBases.length == 0
end
I stripped some parts of the vagrant file (constant definition for exemple) to avoid being too long here, but I think you got the idea.
This is perhaps not the best way, but I found it quite easy to extend/update.
I believe you could just add the two machines to an array and then loop over the array and add the common elements within the loop.
a = config.vm.define "dev", primary: true do |dev|
...
end
b = config.vm.define "pro", primary: true do |pro|
...
end
[a, b].each do |next_vm|
next_vm.provision "chef_client" do |chef|
chef.chef_server_url = "https://example.com/organizations/tsrd"
chef.validation_key_path = "~/my-validator.pem"
chef.validation_client_name = "my-validator"
chef.delete_node = true
chef.delete_client = true
# ... PLUS OTHER COMMON CONFIGURATIONS...
end
end

how to excute code on webrick server

I start a webrick server like this:
dell#dev:/var/www/ruby$ ruby -run -httpd. -p 5000
and have this code in abc.rb:
require 'webrick'
root = File.path '/tmp/public_html'
server = WEBrick::HTTPServer.new :Port => 5000, :DocumentRoot => root
trap 'INT' do server.shutdown end
server.start
ary = { "0"=>"fred", "1"=>10, "2"=>3.14, "3"=>"This is a string", "4"=>"last element", }
ary.each do |key, value|
puts "#{key} #{value}"
end
When I run this code it shows me the same code on browser
http://localhost:5000/abc.rb
How can I view the output this code, I have already asked this question and did not get any correct answer :(
Is it the right code? I want to know this, where this code place
require 'webrick'
root = File.path '/tmp/public_html'
server = WEBrick::HTTPServer.new :Port => 5000, :DocumentRoot => root
trap 'INT' do server.shutdown end
server.start
if any one give me step by step ans to run this code i am very thankful.. I don't understand the ans :( how to do this
From the documentation:
The easiest way to have a server perform custom operations is through
WEBrick::HTTPServer#mount_proc. The block given will be called with a
WEBrick::HTTPRequest with request info and a WEBrick::HTTPResponse
which must be filled in appropriately:
server.mount_proc '/' do |req, res|
res.body = 'Hello, world!'
end
Remember that server.mount_proc must server.start.
So:
require 'webrick'
root = File.path '/tmp/public_html'
server = WEBrick::HTTPServer.new :Port => 5000, :DocumentRoot => root
server.mount_proc '/abc.rb' do |req, res|
ary = { "0"=>"fred", "1"=>10, "2"=>3.14, "3"=>"This is a string", "4"=>"last element" }
res.body = ary.map do |key, value|
"#{key} #{value}"
end.join("\n")
end
trap 'INT' do server.shutdown end
server.start
Also, I believe the correct way to start your WebBrick is by running:
ruby abc.rb

Ruby Tweetstream MongoDB Error

I keep getting the following error when running the following ruby script. If anyone can help me fix this it would be greatly appreciated. I've removed any sensitive data such as API keys.
Code:
#!/usr/bin/env ruby
require "tweetstream"
require "mongo"
require "time"
TweetStream.configure do |config|
config.consumer_key = 'KEY'
config.consumer_secret = 'SECRET'
config.oauth_token = 'TOKEN'
config.oauth_token_secret = 'TOKEN_SECRET'
config.auth_method = :oauth
end
db = Mongo::Connection.new("ds045037.mongolab.com", 45037).db("tweets")
auth = db.authenticate("DB_USERNAME", "DB_PASSWORD")
tweets = db.collection("tweetdata")
TweetStream::Daemon.new("TWITTER_USERNAME", "TWITTER_PASSWORD").track("TERM") do |status|
# Do things when nothing's wrong
data = {"created_at" => Time.parse(status.created_at), "text" => status.text, "geo" => status.geo, "coordinates" => status.coordinates, "id" => status.id, "id_str" => status.id_str}
tweets.insert({"data" => data});
end
Command to start the script:
ruby tweetscrape.rb
Ruby version:
ruby 1.9.3p429 (2013-05-15 revision 40747) [x86_64-linux]
ruby -c tweetscrape.rb produces:
Syntax OK
Error Message:
/usr/local/rvm/gems/ruby-1.9.3-p429/gems/daemons-1.1.9/lib/daemons.rb:184:in `[]=': can't convert Symbol into Integer (TypeError)
from /usr/local/rvm/gems/ruby-1.9.3-p429/gems/daemons-1.1.9/lib/daemons.rb:184:in `run_proc'
from /usr/local/rvm/gems/ruby-1.9.3-p429/gems/tweetstream-2.5.0/lib/tweetstream/daemon.rb:48:in `start'
from /usr/local/rvm/gems/ruby-1.9.3-p429/gems/tweetstream-2.5.0/lib/tweetstream/client.rb:131:in `filter'
from /usr/local/rvm/gems/ruby-1.9.3-p429/gems/tweetstream-2.5.0/lib/tweetstream/client.rb:98:in `track'
from tweetscrape.rb:19:in `<main>'
EDIT: I now have no errors using the below but nothing is entered in to the mongodb:
#!/usr/bin/env ruby
require "tweetstream"
require "mongo"
require "time"
TweetStream.configure do |config|
config.consumer_key = 'gfdsgfdsgfdsgfdsgfdsgfds'
config.consumer_secret = 'gfsdgfdsgfdsgfdsgfsdgfd'
config.oauth_token = 'gfdgfdsgfsdgfdsgfsdgf'
config.oauth_token_secret = 'hsgfsdgfsdgfsdgfds'
config.auth_method = :oauth
end
db = Mongo::Connection.new("ds045037.mongolab.com", 45037).db("tweets")
auth = db.authenticate("gfsdgfdsgfsd", "gfdsgfdsgfdsgfsd")
tweets = db.collection("tweetdata")
TweetStream::Client.new.track('TERM') do |status|
puts status.text
data = {"created_at" => Time.parse(status.created_at), "text" => status.text, "geo" => status.geo, "coordinates" => status.coordinates, "id" => status.id, "id_str" => status.id_str}
tweets.insert({"data" => data})
end
Tweets show on screen through puts though...
The initial error you were getting with the Daemon class is because you're not passing the correct parameters to the constructor. The contructor takes a string and a hash.
Moving on from that , the insert failed because:
parsing status.datetime throws an exception (its already a Time object).
status.coordinate throws an exception if there's no coordinate.
The following code works for me (note : I added growl so you can see the tweets):
#!/usr/bin/env ruby
require "tweetstream"
require "mongo"
require "time"
require 'growl'
DESIRED = %w{created_at text geo coordinates id id_str}
host= ENV["MONGO_HOST"] || 'localhost'
port = ENV["MONGO_PORT"] || 27017
username = ENV["MONGO_USERNAME"]
password = ENV["MONGO_PASSWORD"]
term = ARGV[1] || 'TERM'
begin
TweetStream.configure do |config|
config.consumer_key = ENV["TWEET_CONSUMER_KEY"]
config.consumer_secret = ENV["TWEET_CONSUMER_SECRET"]
config.oauth_token = ENV["TWEET_OAUTH_TOKEN"]
config.oauth_token_secret = ENV["TWEET_OAUTH_TOKEN_SECRET"]
config.auth_method = :oauth
end
db = Mongo::Connection.new(host, port).db("tweets")
db.authenticate(username, password)
tweets = db.collection("tweetdata")
puts "about to start tracking term #{term}"
TweetStream::Daemon.new('tracker').track(term) do |status|
Growl.notify status.text, :title => status.user.screen_name
#
# filter out nil values
# filter out all keys not in the desired array
#
data = status.attrs.select{|k,v| !v.nil? && DESIRED.include?(k.to_s)}
tweets.insert({"data" => data});
end
rescue Mongo::ConnectionFailure
puts "Connection Error : #{$!}"
rescue Mongo::AuthenticationError
puts "Auth Error : #{$!}"
rescue Mongo::MongoDBError
puts "Unexpected Error : #{$!}"
end
You'll need to setup your environment with the following correct values :
export MONGO_USERNAME="..."
export MONGO_PASSWORD="..."
export TWEET_CONSUMER_KEY="..."
export TWEET_CONSUMER_SECRET="..."
export TWEET_OAUTH_TOKEN="..."
export TWEET_OAUTH_TOKEN_SECRET="..."
Then you can start the daemon with something like (in this case we'll search for yankees):
ruby tweetscrape.rb start yankees

Resources