Merge branch 'gildegoma/review-6626'

Related to #6619, #6626 and #6627
This commit is contained in:
Gilles Cornu 2015-12-03 21:52:34 +01:00
commit dde7f4697f
9 changed files with 201 additions and 26 deletions

View File

@ -55,6 +55,8 @@ IMPROVEMENTS:
scripts [GH-6185]
- provisioners/shell: add `env` option [GH-6588, GH-6516]
- provisioners/ansible+ansible_local: add support for ansible-galaxy [GH-2718]
- provisioners/ansible+ansible_local: add support for group and host variables
in the generated inventory [GH-6619]
- provisioners/ansible: add support for WinRM settings [GH-5086]
- provisioners/ansible: add new `force_remote_user` option to control whether
`ansible_ssh_user` parameter should be applied or not [GH-6348]

View File

@ -9,6 +9,7 @@ module VagrantPlugins
attr_accessor :galaxy_role_file
attr_accessor :galaxy_roles_path
attr_accessor :galaxy_command
attr_accessor :host_vars
attr_accessor :groups
attr_accessor :inventory_path
attr_accessor :limit
@ -27,6 +28,7 @@ module VagrantPlugins
@galaxy_role_file = UNSET_VALUE
@galaxy_roles_path = UNSET_VALUE
@galaxy_command = UNSET_VALUE
@host_vars = UNSET_VALUE
@groups = UNSET_VALUE
@inventory_path = UNSET_VALUE
@limit = UNSET_VALUE
@ -46,6 +48,7 @@ module VagrantPlugins
@galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE
@galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE
@galaxy_command = GALAXY_COMMAND_DEFAULT if @galaxy_command == UNSET_VALUE
@host_vars = {} if @host_vars == UNSET_VALUE
@groups = {} if @groups == UNSET_VALUE
@inventory_path = nil if @inventory_path == UNSET_VALUE
@limit = nil if @limit == UNSET_VALUE

View File

@ -70,11 +70,29 @@ module VagrantPlugins
end
end
def get_inventory_host_vars_string(machine_name)
# In Ruby, Symbol and String values are different, but
# Vagrant has to unify them for better user experience.
vars = config.host_vars[machine_name.to_sym]
if !vars
vars = config.host_vars[machine_name.to_s]
end
s = nil
if vars.is_a?(Hash)
s = vars.each.collect{ |k, v| "#{k}=#{v}" }.join(" ")
elsif vars.is_a?(Array)
s = vars.join(" ")
elsif vars.is_a?(String)
s = vars
end
if s and !s.empty? then s else nil end
end
def generate_inventory
inventory = "# Generated by Vagrant\n\n"
# This "abstract" step must fill the @inventory_machines list
# and return the list of supported host(s)
# This "abstract" step must fill the @inventory_machines list
# and return the list of supported host(s)
inventory += generate_inventory_machines
inventory += generate_inventory_groups
@ -88,21 +106,31 @@ module VagrantPlugins
# 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.
def generate_inventory_groups
groups_of_groups = {}
defined_groups = []
group_vars = {}
inventory_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.is_a?(Symbol)
gname = gname.to_s
end
if gmembers.is_a?(String)
gmembers = gmembers.split(/\s+/)
elsif gmembers.is_a?(Hash)
gmembers = gmembers.each.collect{ |k, v| "#{k}=#{v}" }
elsif !gmembers.is_a?(Array)
gmembers = []
end
if gname.end_with?(":children")
groups_of_groups[gname] = gmembers
defined_groups << gname.sub(/:children$/, '')
elsif !gname.include?(':vars')
elsif gname.end_with?(":vars")
group_vars[gname] = gmembers
else
defined_groups << gname
inventory_groups += "\n[#{gname}]\n"
gmembers.each do |gm|
@ -119,6 +147,12 @@ module VagrantPlugins
end
end
group_vars.each_pair do |gname, gmembers|
if defined_groups.include?(gname.sub(/:vars$/, ""))
inventory_groups += "\n[#{gname}]\n" + gmembers.join("\n") + "\n"
end
end
return inventory_groups
end

View File

@ -128,6 +128,8 @@ module VagrantPlugins
else
machines += "#{machine_name}\n"
end
host_vars = get_inventory_host_vars_string(machine_name)
machines.sub!(/\n$/, " #{host_vars}\n") if host_vars
end
end

View File

@ -151,12 +151,15 @@ module VagrantPlugins
# Call only once the SSH and WinRM info computation
# Note that machines configured with WinRM communicator, also have a "partial" ssh_info.
m_ssh_info = m.ssh_info
host_vars = get_inventory_host_vars_string(m.name)
if m.config.vm.communicator == :winrm
m_winrm_net_info = CommunicatorWinRM::Helper.winrm_info(m) # can raise a WinRMNotReady exception...
machines += get_inventory_winrm_machine(m, m_winrm_net_info)
machines.sub!(/\n$/, " #{host_vars}\n") if host_vars
@inventory_machines[m.name] = m
elsif !m_ssh_info.nil?
machines += get_inventory_ssh_machine(m, m_ssh_info)
machines.sub!(/\n$/, " #{host_vars}\n") if host_vars
@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.")

View File

@ -30,6 +30,7 @@ describe VagrantPlugins::Ansible::Config::Host do
galaxy_roles_path
groups
host_key_checking
host_vars
inventory_path
limit
playbook
@ -62,6 +63,7 @@ describe VagrantPlugins::Ansible::Config::Host do
expect(subject.tags).to be_nil
expect(subject.skip_tags).to be_nil
expect(subject.start_at_task).to be_nil
expect(subject.host_vars).to eq({})
expect(subject.groups).to eq({})
expect(subject.host_key_checking).to be_false
expect(subject.raw_arguments).to be_nil

View File

@ -234,42 +234,110 @@ VF
end
end
describe "with host_vars option" do
it_should_create_and_use_generated_inventory
it "adds host variables (given in Hash format) to the generated inventory" do
config.host_vars = {
machine1: {"http_port" => 80, "maxRequestsPerChild" => 808}
}
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
inventory_content = File.read(generated_inventory_file)
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
}
end
it "adds host variables (given in Array format) to the generated inventory" do
config.host_vars = {
machine1: ["http_port=80", "maxRequestsPerChild=808"]
}
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
inventory_content = File.read(generated_inventory_file)
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
}
end
it "adds host variables (given in String format) to the generated inventory " do
config.host_vars = {
:machine1 => "http_port=80 maxRequestsPerChild=808"
}
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
inventory_content = File.read(generated_inventory_file)
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
}
end
it "retrieves the host variables by machine name, also in String format" do
config.host_vars = {
"machine1" => "http_port=80 maxRequestsPerChild=808"
}
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
inventory_content = File.read(generated_inventory_file)
expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808")
}
end
end
describe "with groups option" do
it_should_create_and_use_generated_inventory
it "adds group sections to the generated inventory" do
config.groups = {
"group1" => "#{machine.name}",
"group1:children" => 'bar',
"group1" => "machine1",
"group1:children" => 'bar group3',
"group2" => [iso_env.machine_names[1]],
"group3" => ["unknown", "#{machine.name}"],
group4: [machine.name],
"bar" => ["#{machine.name}", "group3"],
"bar:children" => ["group1", "group2", "group3", "group4"],
"bar:vars" => ["myvar=foo"],
"bar:children" => ["group1", "group2", "group3", "group5"],
}
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
inventory_content = File.read(generated_inventory_file)
# Group variables are intentionally not supported in generated inventory
expect(inventory_content).not_to match(/^\[.*:vars\]$/)
# Accept String instead of Array for group that contains a single item
expect(inventory_content).to include("[group1]\n#{machine.name}\n")
expect(inventory_content).to include("[group1:children]\nbar\n")
# Accept String instead of Array for group member list
expect(inventory_content).to include("[group1]\nmachine1\n\n")
expect(inventory_content).to include("[group1:children]\nbar\ngroup3\n\n")
# Skip "lost" machines
expect(inventory_content).to include("[group2]\n\n")
# Skip "unknown" machines
expect(inventory_content).to include("[group3]\n#{machine.name}\n")
expect(inventory_content).to include("[group3]\n#{machine.name}\n\n")
# Accept Symbol datatype for group names
expect(inventory_content).to include("[group4]\n#{machine.name}\n\n")
# Don't mix group names and host names
expect(inventory_content).to include("[bar]\n#{machine.name}\n")
expect(inventory_content).to include("[bar]\n#{machine.name}\n\n")
# A group of groups only includes declared groups
expect(inventory_content).not_to match(/^group4$/)
expect(inventory_content).to include("[bar:children]\ngroup1\ngroup2\ngroup3\n")
expect(inventory_content).not_to include("group5")
expect(inventory_content).to match(Regexp.quote("[bar:children]\ngroup1\ngroup2\ngroup3\n") + "$")
}
end
it "adds group vars to the generated inventory" do
config.groups = {
"group1" => [machine.name],
"group2" => [machine.name],
"group3" => [machine.name],
"group1:vars" => {"hashvar1" => "hashvalue1", "hashvar2" => "hashvalue2"},
"group2:vars" => ["arrayvar1=arrayvalue1", "arrayvar2=arrayvalue2"],
"group3:vars" => "stringvar1=stringvalue1 stringvar2=stringvalue2",
}
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
inventory_content = File.read(generated_inventory_file)
# Hash syntax
expect(inventory_content).to include("[group1:vars]\nhashvar1=hashvalue1\nhashvar2=hashvalue2\n")
# Array syntax
expect(inventory_content).to include("[group2:vars]\narrayvar1=arrayvalue1\narrayvar2=arrayvalue2\n")
# Single string syntax
expect(inventory_content).to include("[group3:vars]\nstringvar1=stringvalue1\nstringvar2=stringvalue2\n")
}
end
end

View File

@ -31,6 +31,23 @@ Some of these options are for advanced usage only and should not be used unless
```
These variables take the highest precedence over any other variables.
- `host_vars` (hash) - Set of inventory host variables to be included in the [auto-generated inventory file](http://docs.ansible.com/ansible/intro_inventory.html#host-variables).
Example:
```ruby
ansible.host_vars = {
"host1" => {"http_port" => 80,
"maxRequestsPerChild" => 808},
"host2" => {"http_port" => 303,
"maxRequestsPerChild" => 909}
}
```
Notes:
- This option has no effect when the `inventory_path` option is defined.
- `groups` (hash) - Set of inventory groups to be included in the [auto-generated inventory file](/v2/provisioning/ansible_intro.html).
Example:
@ -41,6 +58,15 @@ Some of these options are for advanced usage only and should not be used unless
"db" => ["vm3"]
}
```
Example with [group variables](http://docs.ansible.com/ansible/intro_inventory.html#group-variables):
```ruby
ansible.groups = {
"atlanta" => ["host1", "host2"],
"atlanta:vars" => {"ntp_server" => "ntp.atlanta.example.com",
"proxy" => "proxy.atlanta.example.com"}
}
```
Notes:

View File

@ -107,9 +107,39 @@ default ansible_connection=local
Note that the generated inventory file is uploaded to the guest VM in a subdirectory of [`tmp_path`](/v2/provisioning/ansible_local.html), e.g. `/tmp/vagrant-ansible/inventory/vagrant_ansible_local_inventory`.
**Host variables:**
As of Vagrant 1.8.0, the [`host_vars`](/v2/provisioning/ansible_common.html) option can be used to set [variables for individual hosts](http://docs.ansible.com/ansible/intro_inventory.html#host-variables) in the generated inventory file (see also the notes on group variables below).
```
Vagrant.configure(2) do |config|
config.vm.define "host1"
config.vm.define "host2"
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
ansible.host_vars = {
"host1" => {"http_port" => 80,
"maxRequestsPerChild" => 808},
"host2" => {"http_port" => 303,
"maxRequestsPerChild" => 909}
}
end
end
```
Generated inventory:
```
# Generated by Vagrant
host1 ansible_ssh_host=... http_port=80 maxRequestsPerChild=808
host2 ansible_ssh_host=... http_port=303 maxRequestsPerChild=909
```
**How to generate Inventory Groups:**
The [`groups`](/v2/provisioning/ansible_common.html) option can be used to pass a hash of group names and group members to be included in the generated inventory file.
As of Vagrant 1.8.0, it is also possible to specify [group variables](http://docs.ansible.com/ansible/intro_inventory.html#group-variables).
With this configuration example:
@ -126,7 +156,9 @@ Vagrant.configure(2) do |config|
ansible.groups = {
"group1" => ["machine1"],
"group2" => ["machine2"],
"all_groups:children" => ["group1", "group2"]
"all_groups:children" => ["group1", "group2"],
"group1:vars" => {"variable1" => 9,
"variable2" => "example"}
}
end
end
@ -149,22 +181,25 @@ machine2
[all_groups:children]
group1
group2
[group1:vars]
variable1=9
variable2=example
```
**Notes:**
- Prior to Vagrant 1.7.3, the `ansible_ssh_private_key_file` variable was not set in generated inventory, but passed as command line argument to `ansible-playbook` command.
- The generation of group variables blocks (e.g. `[group1:vars]`) are intentionally not supported, as it is [not recommended to store group variables in the main inventory file](http://docs.ansible.com/intro_inventory.html#splitting-out-host-and-group-specific-data). A good practice is to store these group (or host) variables in `YAML` files stored in `group_vars/` or `host_vars/` directories in the playbook (or inventory) directory.
- The generation of group variables blocks (e.g. `[group1:vars]`) is only possible since Vagrant 1.8.0. Note however that setting variables directly in the inventory is not the [preferred practice in Ansible](http://docs.ansible.com/intro_inventory.html#splitting-out-host-and-group-specific-data). If possible, group (or host) variables should be set in `YAML` files stored in the `group_vars/` or `host_vars/` directories in the playbook (or inventory) directory instead.
- Unmanaged machines and undefined groups are not added to the inventory, to avoid useless Ansible errors (e.g. *unreachable host* or *undefined child group*)
For example, `machine3`, `group3` and `group1:vars` in the example below would not be added to the generated inventory file:
For example, `machine3` and `group3` in the example below would not be added to the generated inventory file:
```
ansible.groups = {
"group1" => ["machine1"],
"group2" => ["machine2", "machine3"],
"all_groups:children" => ["group1", "group2", "group3"],
"group1:vars" => { "variable1" => 9, "variable2" => "example" }
"all_groups:children" => ["group1", "group2", "group3"]
}
```