Can I define the hostname (or other variable) on the vagrant command line? - vagrant

I want to do something like this:
vagrant up --hostname="hello.world"
However there's no --hostname.
The reason I want this is so that the provisioner (Ansible) can use it to modify the provisioning depending on the hostname.
I could also use vagrant up hello.world (in which case Ansible could use it as inventory_hostname), but in that case I'd need to create an entry for hello.world in the Vagrantfile, which I don't want, since all possible hosts use the same Vagrantfile configuration. (An alternative could be to somehow specify in the Vagrantfile "use this configuration regardless of vm-name", but I don't know how to do that either.)
If none of this works, I could use an environment variable, but I don't know how to process that in Vagrantfile either.

You can call vagrant like:
HOSTNAME=myhostname vagrant up
In your Vagrantfile, store the environment variable:
$hostname = #{ENV['HOSTNAME']}"
And then call your ansible playbook passing extra_vars:
ansible.extra_vars = {
hostname: "#{$hostname}"
}
Alternatively, if these hostnames are predefined in groups in your playbook you can use limit:
ansible.limit = "#{$hostname}"

Related

makefile pass variable to vagrantfile and start the vagrant VM

I'm writing the automated deployment plan for the project. I plan to use makefile to control the vagrant vm and its operations. I want to place all user option configurations in the makefile, including some vagrantfile configuration parameters, such as CPU, IP, and the like. But how do I pass makefile parameters to vagrantfile?
I test to use shell
CPU_NUM ?= 3
init:
vagrant_cpu= $(CPU_NUM ) vagrant up
But I didn't know vagrantfile how to obtain it
I would very appreciate it if you guys can tell me how to achieve it that parameters are passed from the makefile to vagrantfile
Makefile is nice, but why not just use shell scripts for general-purpose scripting? But, to actually answer your question:
The command where you set the environment variable is wrong, the = must not have a space on either side. Change it to:
init:
vagrant_cpu=$(CPU_NUM) vagrant up
A Vagrantfile is just a Ruby script. This means that you can access environment variables the same way you would in Ruby, using ENV. In your Vagrantfile, you could have something like:
config.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = ENV["vagrant_cpu"].to_i
end
Note to_i to convert to integer.

How to change ansible verbosity level without changing the command line arguments?

I want to control the verbosity of ansible playbooks using an environment variable or a global configuration item. This is because ansible is called from multiple places in multiple ways and I want to change the logging level for all further executions from the same shell session.
I observed that if I configure ANSIBLE_DEBUG=true ansible will run in debug mode but debug more is extremely verbose and I am only looking for something similar to the -vvv option (DEBUG is more verbose than even -vvvv option)
I tried to look for a variable inside https://github.com/ansible/ansible/blob/devel/lib/ansible/constants.py but I wasn't able to find one this fits the bill.
I see two ways to do this:
alias
alias ansible-playbook="echo 'This is -vv alias'; ansible-playbook -vv"
This way your shell will call ansible-playbook -vv when you type ansible-playbook (and print friendly reminder about alias).
callback plugin
Drop this code as verbosity_env.py file into callback_plugins directory:
from ansible.plugins.callback import CallbackBase
import os
try:
from __main__ import display
except ImportError:
display = None
class CallbackModule(CallbackBase):
def v2_playbook_on_start(self, playbook):
v = os.environ.get('ANSIBLE_VERBOSITY')
if v and display:
display.display('Verbosity is set to {} with environment variable.'.format(v), color='blue')
display.verbosity = int(v)
It is not production quality code, but does the job. This will check ANSIBLE_VERBOSITY environment variable and set display verbosity with its value.
Not sure why I missed to answer this, since a long time ago ansible fully supports ANSIBLE_VERBOSITY=[0|1|2|3|4].
For reference, ansible documentation
You can create/edit ansible.cfg file in the local folder and add in section [defaults]:
[defaults]
verbosity=4
Alternatively you can add the same option in /etc/ansible/ansible.cfg file.
I can't find it documented anywhere other than sorin's answer, but if you set ANSIBLE_VERBOSITY=[0|1|2|3|4] environment variable, ansible-playbook will pick this up and use it, unless you specify the verbosity on the command line.
E.g. in Unix-type shells:
export ANSIBLE_VERBOSITY=2
ansible-playbook my-playbook.yml
I only stumbled upon it because I tried setting ANSIBLE_VERBOSITY='-vv' in a pipeline and Ansible started moaning about it not being an integer!

Extract server IP from the group in the inventory file

I have the following inventory file:
[group_01]
g01_h01 ansible_ssh_host='10.1.0.1'
g01_h01 ansible_ssh_host='10.1.0.2'
[group_02]
g02_h01 ansible_ssh_host='10.2.0.1'
g02_h01 ansible_ssh_host='10.2.0.2'
[group_03:children]
group_01
group_02
[group_03:vars]
fst_group2={{groups['group_02'][0]}}
snd_group1={{groups['group_01'][1]}}
I would like that in my playbook variables had the following values:
fst_group2=10.2.0.1
snd_group1=10.1.0.2
Instead I get:
fst_group2=g02_h01
snd_group1=g01_h02
Any ideas, a workaround?
Very strange task indeed... Anyway,
groups variable – is a list of hosts, which are g01_h01, g01_h02, etc.
To achieve what you expect, you may use this:
[group_03:vars]
fst_group2={{hostvars[groups['group_02'][0]]['ansible_ssh_host']}}
snd_group1={{hostvars[groups['group_01'][1]]['ansible_ssh_host']}}
And keep in mind that ansible_ssh_host is deprecated in favor of ansible_host.

Specifying a particular callback to be used in playbook

I have created different playbooks for different operations in ansible.
And I have also created different Callback Scripts for different kinds of Playbooks (And packaged them with Ansible and installed).
The playbooks will be called from many different scripts/cron jobs.
Now, is it possible to specify a particular callback script to be called for a particular playbook? (Using a command line argument probably?)
What's happening right now is, all the Callback scripts are called for each playbook.
I cannot put the callback script relative to the location/folder of the playbook because it's already packaged inside the ansible package. Also, all the playbooks are in the same location too.
I am fine with modifying a bit of ansible source code to accommodate it too if needed.
After going through the code of Ansible, I was able to solve it with the below...
In each callback_plugin, you can specify self.disabled = True and the callback wont be called at all...
Also, which calling a playbook, there's an option to parsing extra arguments as key=value pairs. It will be part of the playbook object as extra_vars field.
So I did something like this in my callback_plugin.
def playbook_on_start(self):
callback_plugins = self.playbook.extra_vars.get('callback_plugin', '') // self.playbook is populated in your callback plugin by Ansible.
if callback_plugins not in ['email_reporter', 'all']:
self.disabled = True
And while calling the playbook, I can do something like,
ansible-playbook -e callback_plugin=email_reporter //Note -e is the argument prefix key for extra vars.
If with callback scripts you mean callback plugins, you could decide in those plugins if any playbook should trigger some action.
In the playbook_on_play_start method you have the name of the play, which you could use to decide if further notifications should be processed or not.
playbook_on_stats then is called at the end of the playbook.
SHOULD_TRIGGER = false
class CallbackModule(object):
def playbook_on_play_start(self, name):
if name == "My Cool Play":
SHOULD_TRIGGER = true
def playbook_on_stats(self, stats):
if SHOULD_TRIGGER == true:
do something cool
Please note, playbook_on_play_start is called for every play in your playbook, so it might be called multiple times.
If you are simply running a playbook via script you can do something like this
ANSIBLE_STDOUT_CALLBACK="json" ansible-playbook -i hosts play.yml
You are setting the callback as an environment variable prior to ansible-playbook command running.

Run code in Vagrantfile only if provisioning

I want to display some text on the screen when running vagrant up (or vagrant provision, etc.) if and only if provisioning is being done. (For vagrant up it is only run the first time, or if specifically forced with --provision.)
How can this be done?
Adding a shell provisioner is probably the easiest solution, with the small cost that it is executed on the VM over SSH.
Another option is to use the vagrant-host-shell plugin:
Vagrant.configure('2') do |config|
# other config and provisioners
# [...]
config.vm.provision :host_shell, inline: 'echo "Provisioned!"'
end
If you like over-engineering, you can even make your own plugin in Vagrantfile. ;)
class EchoPlugin < Vagrant.plugin('2')
class EchoAction
def initialize(app, env)
#app = app
end
def call(env)
#app.call(env)
puts "Provisioned!"
end
end
name 'echo'
action_hook 'echo' do |hook|
hook.before Vagrant::Action::Builtin::Provision, EchoAction
end
end
Vagrant.configure('2') do |config|
# ...
end
According to the Vagrant issue #7043 where somebody wanted to use #env[:provision_enabled] to see if provisioning is being run. It was answered that you could also check the arguments your Vagrantfile was called with:
This is not currently possible because the Vagrantfile is parsed before the environment is created. This information is available to
provisioners and plugins, but not the Vagrantfile itself because of
the load ordering. In other words, that #env doesn't exist until after
all Vagrantfile's have been parsed, and unfortunately that's a hard
requirement because information in the Vagrantfile determines the way
that object is created. It's a catch-22 for your use case.
One possible alternative is to inspect ARGV in your Vagrantfile.
Something like:
if ARGV.include?("up") || (ARGV.include?("reload") && ARGV.include?("--provision"))
...
end
Example usage
I added two functions to the bottom of my Vagrantfile:
def provisioned?(vm_name='default', provider='virtualbox')
File.exists?(File.join(File.dirname(__FILE__),".vagrant/machines/#{vm_name}/#{provider}/action_provision"))
end
def explicit_provisioning?()
(ARGV.include?("reload") && ARGV.include?("--provision")) || ARGV.include?("provision")
end
Which I can use around any statement in my Vagrantfile:
if (not provisioned?) || explicit_provisioning?
...
end
I'm not sure if I understood your question correctly, but if you want to show a text message if and only if provisioning runs, and you already know that provisioning runs only on first vagrant up and when forcing it using the --provision switch - then why not just add the output of the message to the provisioning itself?
This could be as simple as using a shell provisioner and running an echo command inside of that.
As Vagrant supports multiple provisioners within one Vagrantfile and is able to run all of them when provisioning a virtual machine, this is a dead-easy step, no matter whether you use the shell provisioner anyway, or if you use any other provisioner.

Resources