Merge pull request #2991 from gildegoma/review-pr2926
provisioner/ansible: Ansible single inventory file
This commit is contained in:
commit
a1b29ca82b
|
@ -2,6 +2,7 @@ module VagrantPlugins
|
|||
module Ansible
|
||||
class Provisioner < Vagrant.plugin("2", :provisioner)
|
||||
def provision
|
||||
@logger = Log4r::Logger.new("vagrant::provisioners::ansible")
|
||||
ssh = @machine.ssh_info
|
||||
|
||||
# Connect with Vagrant user (unless --user or --private-key are
|
||||
|
@ -10,6 +11,10 @@ module VagrantPlugins
|
|||
# TODO: multiple private key support
|
||||
options = %W[--private-key=#{ssh[:private_key_path][0]} --user=#{ssh[:username]}]
|
||||
|
||||
# By default we limit by the current machine.
|
||||
# This can be overridden by the limit config option.
|
||||
options << "--limit=#{@machine.name}"
|
||||
|
||||
# Joker! Not (yet) supported arguments can be passed this way.
|
||||
options.concat(self.as_array(config.raw_arguments)) if config.raw_arguments
|
||||
|
||||
|
@ -63,35 +68,61 @@ module VagrantPlugins
|
|||
|
||||
ssh = @machine.ssh_info
|
||||
|
||||
generated_inventory_file =
|
||||
@machine.env.root_path.join("vagrant_ansible_inventory_#{machine.name}")
|
||||
# Managed machines
|
||||
inventory_machines = {}
|
||||
|
||||
generated_inventory_dir = @machine.env.local_data_path.join(File.join(%w(provisioners ansible inventory)))
|
||||
FileUtils.mkdir_p(generated_inventory_dir) unless File.directory?(generated_inventory_dir)
|
||||
generated_inventory_file = generated_inventory_dir.join('vagrant_ansible_inventory')
|
||||
|
||||
generated_inventory_file.open('w') do |file|
|
||||
file.write("# Generated by Vagrant\n\n")
|
||||
file.write("#{machine.name} ansible_ssh_host=#{ssh[:host]} ansible_ssh_port=#{ssh[:port]}\n")
|
||||
|
||||
# Write out groups information. Only include current
|
||||
# machine and its groups to avoid Ansible errors on
|
||||
# provisioning.
|
||||
@machine.env.active_machines.each do |am|
|
||||
begin
|
||||
m = @machine.env.machine(*am)
|
||||
if !m.ssh_info.nil?
|
||||
file.write("#{m.name} ansible_ssh_host=#{m.ssh_info[:host]} ansible_ssh_port=#{m.ssh_info[:port]}\n")
|
||||
inventory_machines[m.name] = m
|
||||
else
|
||||
@logger.error("Auto-generated inventory: Impossible to get SSH information for machine '#{m.name} (#{m.provider_name})'. This machine should be recreated.")
|
||||
# Let a note about this missing machine
|
||||
file.write("# MISSING: '#{m.name}' machine was probably removed without using Vagrant. This machine should be recreated.\n")
|
||||
end
|
||||
rescue Vagrant::Errors::MachineNotFound => e
|
||||
@logger.info("Auto-generated inventory: Skip machine '#{am[0]} (#{am[1]})', which is not configured for this Vagrant environment.")
|
||||
end
|
||||
end
|
||||
|
||||
# Write out groups information.
|
||||
# All defined groups will be included, but only supported
|
||||
# machines and defined child groups will be included.
|
||||
# Group variables are intentionally skipped.
|
||||
groups_of_groups = {}
|
||||
included_groups = []
|
||||
defined_groups = []
|
||||
|
||||
config.groups.each_pair do |gname, gmembers|
|
||||
# Require that gmembers be an array
|
||||
# (easier to be tolerant and avoid error management of few value)
|
||||
gmembers = [gmembers] if !gmembers.is_a?(Array)
|
||||
|
||||
if gname.end_with?(":children")
|
||||
groups_of_groups[gname] = gmembers
|
||||
elsif gmembers.include?("#{machine.name}")
|
||||
included_groups << gname
|
||||
defined_groups << gname.sub(/:children$/, '')
|
||||
elsif !gname.include?(':vars')
|
||||
defined_groups << gname
|
||||
file.write("\n[#{gname}]\n")
|
||||
file.write("#{machine.name}\n")
|
||||
gmembers.each do |gm|
|
||||
file.write("#{gm}\n") if inventory_machines.include?(gm.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defined_groups.uniq!
|
||||
groups_of_groups.each_pair do |gname, gmembers|
|
||||
unless (included_groups & gmembers).empty?
|
||||
file.write("\n[#{gname}]\n")
|
||||
gmembers.each do |gm|
|
||||
file.write("#{gm}\n") if included_groups.include?(gm)
|
||||
end
|
||||
file.write("#{gm}\n") if defined_groups.include?(gm)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ sidebar_current: "provisioning-ansible"
|
|||
**Provisioner name: `"ansible"`**
|
||||
|
||||
The ansible provisioner allows you to provision the guest using
|
||||
[Ansible](http://ansible.cc) playbooks.
|
||||
[Ansible](http://ansible.com) playbooks.
|
||||
|
||||
Ansible playbooks are [YAML](http://en.wikipedia.org/wiki/YAML) documents that
|
||||
comprise the set of steps to be orchestrated on one or more machines. This documentation
|
||||
|
@ -28,21 +28,55 @@ a single page of documentation.
|
|||
## Inventory File
|
||||
|
||||
When using Ansible, it needs to know on which machines a given playbook should run. It does
|
||||
this by way of an inventory file which lists those machines. In the context of Vagrant,
|
||||
there are two ways to approach working with inventory files. The first and simplest option
|
||||
is to not provide one to Vagrant at all. Vagrant will generate inventory files for each
|
||||
virtual machine it manages, and use them for provisioning machines. Generated inventory files
|
||||
are created adjacent to your Vagrantfile, named using the machine names set in your Vagrantfile.
|
||||
this by way of an [inventory](http://docs.ansible.com/intro_inventory.html) file which lists those machines. In the context of Vagrant,
|
||||
there are two ways to approach working with inventory files.
|
||||
|
||||
The second option is for situations where you'd like to have more than one virtual machine
|
||||
in a single inventory file, perhaps leveraging more complex playbooks or inventory grouping.
|
||||
If you provide the `ansible.inventory_path` option referencing a specific inventory file
|
||||
dedicated to your Vagrant project, that one will be used instead of generating them.
|
||||
Such an inventory file for use with Vagrant might look like:
|
||||
The first and simplest option is to not provide one to Vagrant at all. Vagrant will generate an
|
||||
inventory file encompassing all of the virtual machine it manages, and use it for provisioning
|
||||
machines. The generated inventory file is stored as part of your local Vagrant environment in `.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory`.
|
||||
|
||||
The `ansible.groups` option can be used to pass a hash of group
|
||||
names and group members to be included in the generated inventory file. Group variables
|
||||
are intentionally not supported, as this practice is not recommended.
|
||||
For example:
|
||||
|
||||
```
|
||||
[vagrant]
|
||||
192.168.111.222
|
||||
ansible.groups = {
|
||||
"group1" => ["machine1"],
|
||||
"group2" => ["machine2", "machine3"],
|
||||
"all_groups:children" => ["group1", "group2", "group3"]
|
||||
}
|
||||
```
|
||||
|
||||
Note that unmanaged machines and undefined groups are not added to the inventory.
|
||||
For example, `group3` in the above example would not be added to the inventory file.
|
||||
|
||||
A generated inventory might look like:
|
||||
|
||||
```
|
||||
# Generated by Vagrant
|
||||
|
||||
machine1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2200
|
||||
machine2 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2201
|
||||
|
||||
[group1]
|
||||
machine1
|
||||
|
||||
[group2]
|
||||
machine2
|
||||
|
||||
[all_groups:children]
|
||||
group1
|
||||
group2
|
||||
```
|
||||
|
||||
The second option is for situations where you'd like to have more control over the inventory management.
|
||||
With the `ansible.inventory_path` option, you can reference a specific inventory resource (e.g. a static inventory file, a [dynamic inventory script](http://docs.ansible.com/intro_dynamic_inventory.html) or even [multiple inventories stored in the same directory](http://docs.ansible.com/intro_dynamic_inventory.html#using-multiple-inventory-sources)). Vagrant will then use this inventory information instead of generating it.
|
||||
|
||||
A very simple inventory file for use with Vagrant might look like:
|
||||
|
||||
```
|
||||
default ansible_ssh_host=192.168.111.222
|
||||
```
|
||||
|
||||
Where the above IP address is one set in your Vagrantfile:
|
||||
|
@ -51,13 +85,16 @@ Where the above IP address is one set in your Vagrantfile:
|
|||
config.vm.network :private_network, ip: "192.168.111.222"
|
||||
```
|
||||
|
||||
Note that machine names in `Vagrantfile` and `ansible.inventory_path` file should correspond,
|
||||
unless you use `ansible.limit` option to reference the correct machines.
|
||||
|
||||
## Playbook
|
||||
|
||||
The second component of a successful Ansible provisioner setup is the Ansible playbook
|
||||
which contains the steps that should be run on the guest. Ansible's
|
||||
[playbook documentation](http://ansible.cc/docs/playbooks.html) goes into great
|
||||
[playbook documentation](http://docs.ansible.com/playbooks.html) goes into great
|
||||
detail on how to author playbooks, and there are a number of
|
||||
[best practices](http://ansible.cc/docs/bestpractices.html) that can be applied to use
|
||||
[best practices](http://docs.ansible.com/playbooks_best_practices.html) that can be applied to use
|
||||
Ansible's powerful features effectively. A playbook that installs and starts (or restarts
|
||||
if it was updated) the NTP daemon via YUM looks like:
|
||||
|
||||
|
@ -75,7 +112,7 @@ if it was updated) the NTP daemon via YUM looks like:
|
|||
```
|
||||
|
||||
You can of course target other operating systems that don't have YUM by changing the
|
||||
playbook tasks. Ansible ships with a number of [modules](http://ansible.cc/docs/modules.html)
|
||||
playbook tasks. Ansible ships with a number of [modules](http://docs.ansible.com/modules.html)
|
||||
that make running otherwise tedious tasks dead simple.
|
||||
|
||||
## Running Ansible
|
||||
|
@ -90,7 +127,6 @@ Vagrant.configure("2") do |config|
|
|||
end
|
||||
```
|
||||
|
||||
This causes Vagrant to run the `playbook.yml` playbook against all hosts in the inventory file.
|
||||
Since an Ansible playbook can include many files, you may also collect the related files in
|
||||
a directory structure like this:
|
||||
|
||||
|
@ -114,6 +150,13 @@ Vagrant.configure("2") do |config|
|
|||
end
|
||||
```
|
||||
|
||||
Vagrant will try to run the `playbook.yml` playbook against all machines defined in your Vagrantfile.
|
||||
|
||||
**Backward Compatibility Note**:
|
||||
|
||||
Up to Vagrant 1.4, the Ansible provisioner could potentially connect (multiple times) to all hosts from the inventory file.
|
||||
This behaviour is still possible by setting `ansible.limit = 'all'` (see more details below).
|
||||
|
||||
## Additional Options
|
||||
|
||||
The Ansible provisioner also includes a number of additional options that can be set,
|
||||
|
@ -135,7 +178,9 @@ all of which get passed to the `ansible-playbook` command that ships with Ansibl
|
|||
* `ansible.sudo_user` can be set to a string containing a username on the guest who should be used
|
||||
by the sudo command.
|
||||
* `ansible.ask_sudo_pass` can be set to `true` to require Ansible to prompt for a sudo password.
|
||||
* `ansible.limit` can be set to a string or an array of machines or groups from the inventory file to further narrow down which hosts are affected.
|
||||
* `ansible.limit` can be set to a string or an array of machines or groups from the inventory file to further control which hosts are affected. Note that:
|
||||
* As Vagrant 1.5, the machine name (taken from Vagrantfile) is set as **default limit** to ensure that `vagrant provision` steps only affect the expected machine. Setting `ansible.limit` will override this default.
|
||||
* Setting `ansible.limit = 'all'` can be used to make Ansible connects to all machines from the inventory file.
|
||||
* `ansible.verbose` can be set to increase Ansible's verbosity to obtain detailed logging:
|
||||
* `'v'`, verbose mode
|
||||
* `'vv'`
|
||||
|
@ -147,14 +192,56 @@ by the sudo command.
|
|||
* `ansible.raw_arguments` can be set to an array of strings corresponding to a list of `ansible-playbook` arguments (e.g. `['--check', '-M /my/modules']`). It is an *unsafe wildcard* that can be used to apply Ansible options that are not (yet) supported by this Vagrant provisioner. Following precedence rules apply:
|
||||
* Any supported options (described above) will override conflicting `raw_arguments` value (e.g. `--tags` or `--start-at-task`)
|
||||
* Vagrant default user authentication can be overridden via `raw_arguments` (with custom values for `--user` and `--private-key`)
|
||||
* `ansible.groups` can be used to pass a hash of group names and group members to be included in the generated inventory file. For example:
|
||||
|
||||
```
|
||||
ansible.groups = {
|
||||
"group1" => ["machine1"],
|
||||
"group2" => ["machine2", "machine3"],
|
||||
"all_groups:children" => ["group1", "group2"]
|
||||
}
|
||||
```
|
||||
Note that only the current machine and its related groups will be added to the inventory file.
|
||||
* `ansible.host_key_checking` can be set to `false` which will disable host key checking and prevent `"FAILED: (25, 'Inappropriate ioctl for device')"` errors from being reported during provisioner runs. The default value is `true`, which matches the default behavior of Ansible 1.2.1 and later.
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### Ansible Parallel Execution
|
||||
|
||||
Vagrant is designed to provision [multi-machine environments](/v2/multi-machine) in sequence, but the following configuration pattern can be used to take advantage of Ansible parallelism:
|
||||
|
||||
```
|
||||
config.vm.define 'machine2' do |machine|
|
||||
machine.vm.hostname = 'machine2'
|
||||
machine.vm.network "private_network", ip: "192.168.77.22"
|
||||
end
|
||||
|
||||
config.vm.define 'machine1' do |machine|
|
||||
machine.vm.hostname = 'machine1'
|
||||
machine.vm.network "private_network", ip: "192.168.77.21"
|
||||
|
||||
machine.vm.provision :ansible do |ansible|
|
||||
ansible.playbook = "playbook.yml"
|
||||
|
||||
# Disable default limit (required with Vagrant 1.5+)
|
||||
ansible.limit = 'all'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Provide a local `ansible.cfg` file
|
||||
|
||||
Certain settings in Ansible are (only) adjustable via a [configuration file](http://docs.ansible.com/intro_configuration.html), and you might want to ship such a file in your Vagrant project.
|
||||
|
||||
As `ansible-playbook` command looks for local [`ansible.cfg`] configuration file in its *current directory* (but not in the directory that contains the main playbook), you have to store this file adjacent to your Vagrantfile.
|
||||
|
||||
Note that it is also possible to reference an Ansible configuration file via `ANSIBLE_CONFIG` environment variable, if you want to be flexible about the location of this file.
|
||||
|
||||
### Why Ansible provisioner does connect with a wrong user ?
|
||||
|
||||
It is good to know that following Ansible settings always override `config.ssh.username` option defined in [Vagrant SSH Settings](/v2/vagrantfile/ssh_settings.html):
|
||||
|
||||
* `ansible_ssh_user` variable
|
||||
* `remote_user` (or `user`) play attribute
|
||||
* `remote_user` task attribute
|
||||
|
||||
Be aware that copying snippets from Ansible documentation might lead to this problem, as `root` is used as remote user in many [examples](http://docs.ansible.com/playbooks_intro.html#hosts-and-users).
|
||||
|
||||
Example of SSH error (with `vvv` log level), where an undefined remote user `xyz` has replaced `vagrant`:
|
||||
|
||||
```
|
||||
TASK: [my_role | do something] *****************
|
||||
<127.0.0.1> ESTABLISH CONNECTION FOR USER: xyz
|
||||
<127.0.0.1> EXEC ['ssh', '-tt', '-vvv', '-o', 'ControlMaster=auto',...
|
||||
fatal: [ansible-devbox] => SSH encountered an unknown error. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue.
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue