lib/zergrush_vagrant/renderer.rb in zergrush_vagrant-0.0.1 vs lib/zergrush_vagrant/renderer.rb in zergrush_vagrant-0.0.2

- old
+ new

@@ -22,28 +22,22 @@ #++ require 'awesome_print' require 'fileutils' require 'securerandom' +require 'ipaddress' +require 'digest/sha1' +require 'ipaddress' require_relative 'erbalize' class Renderer - # generate a virtualbox - compatible MAC address - def generateMACAddress() - firstChar = (0..255).map(&:chr).select{|x| x =~ /[0-9A-Fa-f]/}.sample(1).join - secondChar = (0..255).map(&:chr).select{|x| x =~ /[02468ACEace]/}.sample(1).join - restOfChars = (0..255).map(&:chr).select{|x| x =~ /[0-9A-Fa-f]/}.sample(10).join - return "#{firstChar}#{secondChar}#{restOfChars}" - end - def initialize(hive_location, task_name, task_hash) @vm = task_hash["vm"] @name = task_name - @instances = task_hash["instances"] - @tasks = task_hash["tasks"] - @synced_folders = task_hash["synced_folders"] + @num_instances = task_hash["num_instances"] + @vm_instances = task_hash["vm"]["instances"] @hive_location = hive_location end def render puts ("Rendering driver templates...") @@ -55,116 +49,230 @@ provider_parent_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "resources", "provider.template"), 'r').read # load the machine details template machine_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "resources", "machine.template"), 'r').read - # load the bridge details template - bridge_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "resources", "bridging.template"), 'r').read - # load the host only network details template - hostonly_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "resources", "hostonly.template"), 'r').read - # render templates.... - # render provider details to string - # - # render provider details into a string - provider_details_array = @vm["driver"]["provider_options"] - provider_details = "" - for index in 0..provider_details_array.length - 1 - provider_details += "\t\t" + provider_details_array[index] + "\n" + + # all machines + all_machines = "" + + # JSON - defined range of ip addresses in CIDR format + ip_range = (@vm.has_key?("private_ip_range")) ? IPAddress(@vm["private_ip_range"]).hosts : nil + if ip_range != nil + abort("ERROR: ip range (#{@vm["private_ip_range"]}) does not have enough ip addresses for all instances.") unless ip_range.length > @num_instances end - # render provider parent - sources = { - :provider => @vm["driver"]["providertype"], - :provider_specifics => provider_details - } - provider_parent_string = Erbalize.erbalize_hash(provider_parent_template, sources) + # last explicitly defined vm instance. + last_defined_vm = nil - # render machine template - all_macs = Array.new - all_machines = "" - for index in 0..@instances - 1 + # last explicitly defined driver option set + last_defined_driveroption = nil - # last ip octet offset for host only networking - ip_octet_offset = index + # render a machine section for each instance. + # each machine gets an explicitly defined instance + # if there is no explicitly defined instance - last known explicitly defined instance information is used. + # For example: if num_instances = 3 and there are 2 vm instances defined, then machine_0 gets instance definition 0, machine_1 gets instance + # definition 1, machine_2 gets instance definition 2 + for index in 0..@num_instances - 1 - # inject randomized node_name into chef_client tasks - @tasks.each { |task| - if task["type"] == "chef_client" - task["node_name"] = "zergling_#{index}_#{SecureRandom.hex(20)}" + # grab last defined vm instance, or keep the current one + last_defined_vm = (@vm_instances[index] == nil) ? last_defined_vm : @vm_instances[index] + + # grab last defined driver options set, or keep the current one + last_defined_driveroption = (@vm["driver"]["driveroptions"][index] == nil) ? last_defined_driveroption : @vm["driver"]["driveroptions"][index] + + # provider type + provider = last_defined_driveroption["providertype"] + + # unique name + unique_name = "zergling_#{index}_#{SecureRandom.hex(20)}" + + # render provider details to string + provider_specifics = "" + + if provider == "aws" + # inject private ip for aws provider (if not specified explicitly and if ip range is provided) + if last_defined_driveroption.has_key?("provider_options") + if last_defined_driveroption["provider_options"].has_key?("subnet_id") + if !last_defined_driveroption["provider_options"].has_key?("private_ip_address") + if ip_range != nil + provider_specifics += "\t\t\t#{provider}.private_ip_address = #{ip_range[index]}" + end + end + end end - } - # tasks array rendered to ruby string. double encoding to escape quotes and allow for variable expansion - tasks_array = @tasks.to_json.to_json + # inject name tag + if last_defined_driveroption.has_key?("provider_options") + if last_defined_driveroption["provider_options"].has_key?("tags") + if !last_defined_driveroption["provider_options"]["tags"].has_key?("Name") + last_defined_driveroption["provider_options"]["tags"]["Name"] = unique_name + end + else + last_defined_driveroption["provider_options"]["tags"] = { "Name" => unique_name } + end + end - # do we need the bridging template as well? - bridge_section = nil - if @vm.has_key?("bridge_description") - # mac address to use? - new_mac = "" - begin - new_mac = generateMACAddress() - end while all_macs.include? new_mac + end - sources = { - :machine_mac => new_mac, - :bridged_eth_description => @vm["bridge_description"] + if last_defined_driveroption.has_key?("provider_options") + provider_options = last_defined_driveroption["provider_options"] + + provider_options.each do |key, value| + if value.is_a?(String) + provider_specifics += "\t\t\t#{provider}.#{key} = \"#{value}\"\n" + elsif value.is_a?(Array) + provider_specifics += "\t\t\t#{provider}.#{key} = #{value.to_json}\n" + else + provider_specifics += "\t\t\t#{provider}.#{key} = #{value}\n" + end + end + end + + if last_defined_driveroption.has_key?("raw_options") + raw_provider_options = last_defined_driveroption["raw_options"] + raw_provider_options.each { |raw_option| + provider_specifics += "\t\t\t#{raw_option}\n" } - bridge_section = Erbalize.erbalize_hash(bridge_template, sources) end - # do we need the host only template as well? - hostonly_section = nil - if @vm["private_network"] == true - sources = { - :machine_name => "zergling_#{index}", - :last_octet => ip_octet_offset + 4, # TODO: this is probably specific to virtualbox networking + # render networks + network_specifics = "" + if last_defined_vm.has_key?("networks") + last_defined_vm["networks"].each { |network| + network_specifics += "\t\tzergling_#{index}.vm.network \"#{network["type"]}\"" + if network.has_key?("bridge") + network_specifics += ", bridge: \"#{network["bridge"]}\"" + end + if network.has_key?("ip") + network_specifics += ", ip: \"#{network["ip"]}\"" + elsif ip_range != nil && network["type"] != "public_network" + # first host IP is the host machine + network_specifics += ", ip: \"#{ip_range[index + 1]}\"" + end + + if network.has_key?("additional") + network["additional"].each do |key, value| + if value.is_a?(String) + network_specifics += ", #{key}: \"#{value}\"" + else + network_specifics += ", #{key}: #{value}" + end + end + end + network_specifics += "\n" } - hostonly_section = Erbalize.erbalize_hash(hostonly_template, sources) end - # synced folders - folder_definitions = nil - if @synced_folders != nil - folder_definitions = "" - @synced_folders.each { |folder| - other_options = "" - if folder.has_key?("options") - folder["options"].each { |option| + # render sync folders + folder_specifics = "" + if last_defined_vm.has_key?("synced_folders") + last_defined_vm["synced_folders"].each { |folder| + folder_specifics += "\t\tzergling_#{index}.vm.synced_folder \"#{folder["host_path"]}\", \"#{folder["guest_path"]}\"" + if folder.has_key?("additional") + folder["additional"].each do |key, value| + if value.is_a?(String) + folder_specifics += ", #{key}: \"#{value}\"" + else + folder_specifics += ", #{key}: #{value}" + end + end + end + folder_specifics += "\n" + } + end + + # render forwarded ports + port_specifics = "" + if last_defined_vm.has_key?("forwarded_ports") + last_defined_vm["forwarded_ports"].each { |port| + port_specifics += "\t\tzergling_#{index}.vm.network \"forwarded_port\", guest: #{port["guest_port"]}, host: #{port["host_port"]}" + if port.has_key?("additional") + port["additional"].each { |option| option.each do |key, value| if value.is_a?(String) - other_options += ", :#{key} => \"#{value}\"" + port_specifics += ", #{key}: \"#{value}\"" else - other_options += ", :#{key} => #{value}" + port_specifics += ", #{key}: #{value}" end end } end - - folder_definition = "zergling_#{index}.vm.synced_folder \"#{folder['host_path']}\", \"#{folder['guest_path']}\"" - folder_definition = "#{folder_definition}#{other_options}" unless other_options.empty?() - folder_definitions += "\t\t#{folder_definition}\n" + port_specifics += "\n" } end + # render ssh settings + ssh_specifics = "" + if last_defined_vm.has_key?("ssh") + if last_defined_vm["ssh"].has_key?("username") + ssh_specifics += "\t\tzergling_#{index}.ssh.username = \"#{last_defined_vm["ssh"]["username"]}\"\n" + end + + if last_defined_vm["ssh"].has_key?("host") + ssh_specifics += "\t\tzergling_#{index}.ssh.host = \"#{last_defined_vm["ssh"]["host"]}\"\n" + end + + if last_defined_vm["ssh"].has_key?("port") + ssh_specifics += "\t\tzergling_#{index}.ssh.port = #{last_defined_vm["ssh"]["port"]}\n" + end + + if last_defined_vm["ssh"].has_key?("guest_port") + ssh_specifics += "\t\tzergling_#{index}.ssh.guest_port = #{last_defined_vm["ssh"]["guest_port"]}\n" + end + + if last_defined_vm["ssh"].has_key?("private_key_path") + ssh_specifics += "\t\tzergling_#{index}.ssh.private_key_path = \"#{last_defined_vm["ssh"]["private_key_path"]}\"\n" + end + + if last_defined_vm["ssh"].has_key?("forward_agent") + ssh_specifics += "\t\tzergling_#{index}.ssh.forward_agent = #{last_defined_vm["ssh"]["forward_agent"]}\n" + end + + if last_defined_vm["ssh"].has_key?("additional") + last_defined_vm["ssh"]["additional"].each { |option| + option.each do |key, value| + if value.is_a?(String) + ssh_specifics += "\t\tzergling_#{index}.ssh.#{key} = \"#{value}\"\n" + else + ssh_specifics += "\t\tzergling_#{index}.ssh.#{key} = #{value}\n" + end + end + } + end + end + + # render tasks array + # inject randomized node_name into chef_client tasks + last_defined_vm["tasks"].each { |task| + if task["type"] == "chef_client" + task["node_name"] = unique_name + end + } + + # tasks array rendered to ruby string. double encoding to escape quotes and allow for variable expansion + tasks_array = last_defined_vm["tasks"].to_json.to_json + sources = { :machine_name => "zergling_#{index}", - :bridge_specifics => bridge_section, - :hostonly_specifics => hostonly_section, - :tasks_array => tasks_array, - :sync_folders_array => folder_definitions + :basebox_path => last_defined_vm["basebox"], + :box_name => Digest::SHA1.hexdigest("#{@name}#{provider}#{last_defined_vm["basebox"]}"), + :provider => provider, + :provider_specifics => provider_specifics, + :networks_array => network_specifics, + :sync_folders_array => folder_specifics, + :ports_array => port_specifics, + :ssh_specifics => ssh_specifics, + :tasks_array => tasks_array }.delete_if { |k, v| v.nil? } machine_section = Erbalize.erbalize_hash(machine_template, sources) all_machines += "\n#{machine_section}" end sources = { - :provider_section => provider_parent_string, - :basebox_path => @vm["basebox"], - :box_name => "zergling_#{@name}_#{@vm["driver"]["providertype"]}", :vm_defines => all_machines } full_template = Erbalize.erbalize_hash(main_template, sources) # write the file