PageRenderTime 1671ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/provisioners/ansible/provisioner/base.rb

https://gitlab.com/thomasphillips3/vagrant
Ruby | 248 lines | 176 code | 39 blank | 33 comment | 24 complexity | 6d289890ce64e68b746846d8649c9f5c MD5 | raw file
  1. require_relative "../errors"
  2. require_relative "../helpers"
  3. module VagrantPlugins
  4. module Ansible
  5. module Provisioner
  6. # This class is a base class where the common functionality shared between
  7. # both Ansible provisioners are stored.
  8. # This is **not an actual provisioner**.
  9. # Instead, {Host} (ansible) or {Guest} (ansible_local) should be used.
  10. class Base < Vagrant.plugin("2", :provisioner)
  11. RANGE_PATTERN = %r{(?:\[[a-z]:[a-z]\]|\[[0-9]+?:[0-9]+?\])}.freeze
  12. protected
  13. def initialize(machine, config)
  14. super
  15. @command_arguments = []
  16. @environment_variables = {}
  17. @inventory_machines = {}
  18. @inventory_path = nil
  19. end
  20. def ansible_playbook_command_for_shell_execution
  21. shell_command = []
  22. @environment_variables.each_pair do |k, v|
  23. if k == 'ANSIBLE_SSH_ARGS'
  24. shell_command << "#{k}='#{v}'"
  25. else
  26. shell_command << "#{k}=#{v}"
  27. end
  28. end
  29. shell_command << "ansible-playbook"
  30. shell_args = []
  31. @command_arguments.each do |arg|
  32. if arg =~ /(--start-at-task|--limit)=(.+)/
  33. shell_args << %Q(#{$1}="#{$2}")
  34. elsif arg =~ /(--extra-vars)=(.+)/
  35. shell_args << %Q(%s="%s") % [$1, $2.gsub('\\', '\\\\\\').gsub('"', %Q(\\"))]
  36. else
  37. shell_args << arg
  38. end
  39. end
  40. shell_command << shell_args
  41. # Add the raw arguments at the end, to give them the highest precedence
  42. shell_command << config.raw_arguments if config.raw_arguments
  43. shell_command << config.playbook
  44. shell_command.flatten.join(' ')
  45. end
  46. def prepare_common_command_arguments
  47. # By default we limit by the current machine,
  48. # but this can be overridden by the `limit` option.
  49. if config.limit
  50. @command_arguments << "--limit=#{Helpers::as_list_argument(config.limit)}"
  51. else
  52. @command_arguments << "--limit=#{@machine.name}"
  53. end
  54. @command_arguments << "--inventory-file=#{inventory_path}"
  55. @command_arguments << "--extra-vars=#{extra_vars_argument}" if config.extra_vars
  56. @command_arguments << "--sudo" if config.sudo
  57. @command_arguments << "--sudo-user=#{config.sudo_user}" if config.sudo_user
  58. @command_arguments << "#{verbosity_argument}" if verbosity_is_enabled?
  59. @command_arguments << "--vault-password-file=#{config.vault_password_file}" if config.vault_password_file
  60. @command_arguments << "--tags=#{Helpers::as_list_argument(config.tags)}" if config.tags
  61. @command_arguments << "--skip-tags=#{Helpers::as_list_argument(config.skip_tags)}" if config.skip_tags
  62. @command_arguments << "--start-at-task=#{config.start_at_task}" if config.start_at_task
  63. end
  64. def prepare_common_environment_variables
  65. # Ensure Ansible output isn't buffered so that we receive output
  66. # on a task-by-task basis.
  67. @environment_variables["PYTHONUNBUFFERED"] = 1
  68. # When Ansible output is piped in Vagrant integration, its default colorization is
  69. # automatically disabled and the only way to re-enable colors is to use ANSIBLE_FORCE_COLOR.
  70. @environment_variables["ANSIBLE_FORCE_COLOR"] = "true" if @machine.env.ui.color?
  71. # Setting ANSIBLE_NOCOLOR is "unnecessary" at the moment, but this could change in the future
  72. # (e.g. local provisioner [GH-2103], possible change in vagrant/ansible integration, etc.)
  73. @environment_variables["ANSIBLE_NOCOLOR"] = "true" if !@machine.env.ui.color?
  74. end
  75. # Auto-generate "safe" inventory file based on Vagrantfile,
  76. # unless inventory_path is explicitly provided
  77. def inventory_path
  78. if config.inventory_path
  79. config.inventory_path
  80. else
  81. @inventory_path ||= generate_inventory
  82. end
  83. end
  84. def get_inventory_host_vars_string(machine_name)
  85. # In Ruby, Symbol and String values are different, but
  86. # Vagrant has to unify them for better user experience.
  87. vars = config.host_vars[machine_name.to_sym]
  88. if !vars
  89. vars = config.host_vars[machine_name.to_s]
  90. end
  91. s = nil
  92. if vars.is_a?(Hash)
  93. s = vars.each.collect{ |k, v| "#{k}=#{v}" }.join(" ")
  94. elsif vars.is_a?(Array)
  95. s = vars.join(" ")
  96. elsif vars.is_a?(String)
  97. s = vars
  98. end
  99. if s and !s.empty? then s else nil end
  100. end
  101. def generate_inventory
  102. inventory = "# Generated by Vagrant\n\n"
  103. # This "abstract" step must fill the @inventory_machines list
  104. # and return the list of supported host(s)
  105. inventory += generate_inventory_machines
  106. inventory += generate_inventory_groups
  107. # This "abstract" step must create the inventory file and
  108. # return its location path
  109. # TODO: explain possible race conditions, etc.
  110. @inventory_path = ship_generated_inventory(inventory)
  111. end
  112. # Write out groups information.
  113. # All defined groups will be included, but only supported
  114. # machines and defined child groups will be included.
  115. def generate_inventory_groups
  116. groups_of_groups = {}
  117. defined_groups = []
  118. group_vars = {}
  119. inventory_groups = ""
  120. # Verify if host range patterns exist and warn
  121. if config.groups.any? { |gm| gm.to_s[RANGE_PATTERN] }
  122. @machine.ui.warn(I18n.t("vagrant.provisioners.ansible.ansible_host_pattern_detected"))
  123. end
  124. config.groups.each_pair do |gname, gmembers|
  125. if gname.is_a?(Symbol)
  126. gname = gname.to_s
  127. end
  128. if gmembers.is_a?(String)
  129. gmembers = gmembers.split(/\s+/)
  130. elsif gmembers.is_a?(Hash)
  131. gmembers = gmembers.each.collect{ |k, v| "#{k}=#{v}" }
  132. elsif !gmembers.is_a?(Array)
  133. gmembers = []
  134. end
  135. if gname.end_with?(":children")
  136. groups_of_groups[gname] = gmembers
  137. defined_groups << gname.sub(/:children$/, '')
  138. elsif gname.end_with?(":vars")
  139. group_vars[gname] = gmembers
  140. else
  141. defined_groups << gname
  142. inventory_groups += "\n[#{gname}]\n"
  143. gmembers.each do |gm|
  144. # TODO : Expand and validate host range patterns
  145. # against @inventory_machines list before adding them
  146. # otherwise abort with an error message
  147. if gm[RANGE_PATTERN]
  148. inventory_groups += "#{gm}\n"
  149. end
  150. inventory_groups += "#{gm}\n" if @inventory_machines.include?(gm.to_sym)
  151. end
  152. end
  153. end
  154. defined_groups.uniq!
  155. groups_of_groups.each_pair do |gname, gmembers|
  156. inventory_groups += "\n[#{gname}]\n"
  157. gmembers.each do |gm|
  158. inventory_groups += "#{gm}\n" if defined_groups.include?(gm)
  159. end
  160. end
  161. group_vars.each_pair do |gname, gmembers|
  162. if defined_groups.include?(gname.sub(/:vars$/, ""))
  163. inventory_groups += "\n[#{gname}]\n" + gmembers.join("\n") + "\n"
  164. end
  165. end
  166. return inventory_groups
  167. end
  168. def extra_vars_argument
  169. if config.extra_vars.kind_of?(String) and config.extra_vars =~ /^@.+$/
  170. # A JSON or YAML file is referenced.
  171. config.extra_vars
  172. else
  173. # Expected to be a Hash after config validation.
  174. config.extra_vars.to_json
  175. end
  176. end
  177. def get_galaxy_role_file(base_dir)
  178. Helpers::expand_path_in_unix_style(config.galaxy_role_file, base_dir)
  179. end
  180. def get_galaxy_roles_path(base_dir)
  181. if config.galaxy_roles_path
  182. Helpers::expand_path_in_unix_style(config.galaxy_roles_path, base_dir)
  183. else
  184. playbook_path = Helpers::expand_path_in_unix_style(config.playbook, base_dir)
  185. File.join(Pathname.new(playbook_path).parent, 'roles')
  186. end
  187. end
  188. def ui_running_ansible_command(name, command)
  189. @machine.ui.detail I18n.t("vagrant.provisioners.ansible.running_#{name}")
  190. if verbosity_is_enabled?
  191. # Show the ansible command in use
  192. @machine.env.ui.detail command
  193. end
  194. end
  195. def verbosity_is_enabled?
  196. config.verbose && !config.verbose.to_s.empty?
  197. end
  198. def verbosity_argument
  199. if config.verbose.to_s =~ /^-?(v+)$/
  200. "-#{$+}"
  201. else
  202. # safe default, in case input strays
  203. '-v'
  204. end
  205. end
  206. end
  207. end
  208. end
  209. end