diff --git a/README.md b/README.md index bbf2ff3..0e4e82c 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,15 @@ [Vagrant](http://www.vagrantup.com) provider for VMware vCloud Director® ============= -[Version 0.3.3](../../releases/tag/v0.3.3) has been released! +[Version 0.4.0](../../releases/tag/v0.4.0) has been released! ------------- Please note that this software is still Alpha/Beta quality and is not recommended for production usage. -Right now a [Precise32](http://vagrant.tsugliani.fr/precise32.box) is available for use, or you can roll your own as you please, make sure to install VMware tools in it. +We have a wide array of boxes available at [Vagrant Cloud](https://vagrantcloud.com/gosddc) you can use them directly or you can roll your own as you please, make sure to install VMware tools in it. If you're unsure about what are the correct network settings for your Vagrantfile make sure to check out the [Network Deployment Options](https://github.com/frapposelli/vagrant-vcloud/wiki/Network-Deployment-Options) wiki page. -Features of Version 0.3.3 are: - -- Critical Bug Fix for ```network_bridge``` users [[#67](../../issues/67)] - -Features of Version 0.3.2 are: - -- Added support for ```vagrant share``` command [[#31](../../issues/31)] *experimental* -- Restructured the ```vagrant vcloud-status``` command to ```vagrant vcloud``` namespace for future-proofing [[#53](../../issues/53)] -- Added ```vagrant vcloud --redeploy-edge-gw``` to redeploy Edge Gateway [[#54](../../issues/54)] -- Fixed Issues [[#45](https://github.com/frapposelli/vagrant-vcloud/issues/45), [#46](https://github.com/frapposelli/vagrant-vcloud/issues/46), [#47](https://github.com/frapposelli/vagrant-vcloud/issues/47), [#48](https://github.com/frapposelli/vagrant-vcloud/issues/48), [#51](https://github.com/frapposelli/vagrant-vcloud/issues/51), [#52](https://github.com/frapposelli/vagrant-vcloud/issues/52), [#56](https://github.com/frapposelli/vagrant-vcloud/issues/56), [#57](https://github.com/frapposelli/vagrant-vcloud/issues/57), [#61](https://github.com/frapposelli/vagrant-vcloud/issues/61)] - - -Features of Version 0.3.1 are: - -- Small hotfix to include "preRunning" condition when using vCloud Director 5.5 [[#44](https://github.com/frapposelli/vagrant-vcloud/issues/44)]. - [Andrew Poland](https://github.com/apoland) - -Features of Version 0.3.0 are: - -A substantial release, major kudos to [Stefan Scherer](https://github.com/StefanScherer) who submitted some substantious PRs! - -- Added support for port mapping at the Organization Edge Gateway. -- Added a new configuration options ```vapp_prefix``` to change vApp prefix (defaults to Vagrant). -- Improved vcloud-status command. -- Fixed cygdrive path for rsync on Windows. -- Fixed Issue [[#33](../../issues/33)] - Error removing/creating NAT rules on second vagrant up. -- Fixed Issue [[#43](../../issues/43)] - Destroy fails if VMs are halted. - -Features of Version 0.2.2 are: - -- Fixed Issue [[#32](../../issues/32)] - Port Forwarding rules are deleted when Halting a VM. - -Features of Version 0.2.1 are: - -- Critical Bugfixes - -Features of Version 0.2.0 are: - -- It's now possible to connect to an existing VDC network without creating a vShield Edge using ```network_bridge = true``` in the Vagrantfile [[#23](../../issues/23)]. *experimental* -- Added a ```upload_chunksize``` parameter to specify the chunk dimension during box uploads [[#21](../../issues/21)]. -- Added support for [vCloud® Hybrid Service™](http://www.vmware.com/products/vcloud-hybrid-service) API version 5.7. -- Added a new command to vagrant called ```vcloud-status``` that shows the current status of the vCloud instance relative to the Vagrant deployment. *experimental* -- General code cleanup, code should be more readable and there's a rubocop file for our code conventions. -- Passwords are now hidden when running in DEBUG mode. -- Initial support for Vagrant 1.5 (currently not supporting the new "share" features). -- Lowered Nokogiri requirement to 1.5.5 (you may need to remove a later version if installed). -- Fixed the Edge Gateway NAT rules creation / deletion. -- Added debug capabilities down to XML traffic exchanged during the REST calls. - - Check the full releases changelog [here](../../releases) Install @@ -78,36 +29,33 @@ Configuration Here's a sample Multi-VM Vagrantfile, please note that ```vcloud.vdc_edge_gateway``` and ```vcloud.vdc_edge_gateway_ip``` are required only when you cannot access ```vcloud.vdc_network_name``` directly and there's an Organization Edge between your workstation and the vCloud Network. ```ruby -precise32_vm_box_url = "http://vagrant.tsugliani.fr/precise32.box" - nodes = [ - { :hostname => "web-vm", :box => "precise32", :box_url => precise32_vm_box_url }, - { :hostname => "ssh-vm", :box => "precise32", :box_url => precise32_vm_box_url }, - { :hostname => "sql-vm", :box => "precise32", :box_url => precise32_vm_box_url }, - { :hostname => "lb-vm", :box => "precise64", :box_url => precise32_vm_box_url }, - { :hostname => "app-vm", :box => "precise32", :box_url => precise32_vm_box_url }, + { hostname: 'web-vm', box: 'gosddc/precise32' }, + { hostname: 'ssh-vm', box: 'gosddc/precise32' }, + { hostname: 'sql-vm', box: 'gosddc/precise32' }, + { hostname: 'app-vm', box: 'gosddc/precise32' } ] -Vagrant.configure("2") do |config| +Vagrant.configure('2') do |config| # vCloud Director provider settings config.vm.provider :vcloud do |vcloud| - vcloud.vapp_prefix = "multibox-sample" - - vcloud.hostname = "https://my.cloudprovider.com" - vcloud.username = "MyUserName" - vcloud.password = "MySup3rS3cr3tPassw0rd!" - - vcloud.org_name = "OrganizationName" - vcloud.vdc_name = "vDC_Name" - - vcloud.catalog_name = "Vagrant" - vcloud.ip_subnet = "172.16.32.125/255.255.255.240" - - vcloud.vdc_network_name = "MyNetwork" - - vcloud.vdc_edge_gateway = "MyOrgEdgeGateway" - vcloud.vdc_edge_gateway_ip = "10.10.10.10" + vcloud.vapp_prefix = 'multibox-sample' + + vcloud.hostname = 'https://my.cloudprovider.com' + vcloud.username = 'MyUserName' + vcloud.password = 'MySup3rS3cr3tPassw0rd!' + + vcloud.org_name = 'OrganizationName' + vcloud.vdc_name = 'vDC_Name' + + vcloud.catalog_name = 'Vagrant' + vcloud.ip_subnet = '172.16.32.125/255.255.255.240' + + vcloud.vdc_network_name = 'MyNetwork' + + vcloud.vdc_edge_gateway = 'MyOrgEdgeGateway' + vcloud.vdc_edge_gateway_ip = '10.10.10.10' end nodes.each do |node| @@ -115,7 +63,10 @@ Vagrant.configure("2") do |config| node_config.vm.box = node[:box] node_config.vm.hostname = node[:hostname] node_config.vm.box_url = node[:box_url] - node_config.vm.network :forwarded_port, guest: 80, host: 8080, auto_correct: true + node_config.vm.network :forwarded_port, + guest: 80, + host: 8080, + auto_correct: true # node_config.vm.provision :puppet do |puppet| # puppet.manifests_path = 'puppet/manifests' # puppet.manifest_file = 'site.pp' diff --git a/lib/vagrant-vcloud/action.rb b/lib/vagrant-vcloud/action.rb index 094f130..75fc58a 100644 --- a/lib/vagrant-vcloud/action.rb +++ b/lib/vagrant-vcloud/action.rb @@ -96,21 +96,28 @@ def self.action_destroy if env[:result] b2.use ConfigValidate b2.use ConnectVCloud - b2.use Call, IsRunning do |env2, b3| - # If the VM is running, must power off - b3.use action_halt if env2[:result] - end - b2.use Call, IsLastVM do |env2, b3| - if env2[:result] - # Check if the network is bridged - b3.use Call, IsBridged do |env3, b4| - # if it's not, delete port forwardings. - b4.use UnmapPortForwardings unless env3[:bridged_network] + b2.use Call, IsCreated do |env2, b3| + unless env2[:result] + b3.use MessageNotCreated + next + end + + b3.use Call, IsRunning do |env3, b4| + # If the VM is running, must power off + b4.use action_halt if env3[:result] + end + b3.use Call, IsLastVM do |env3, b4| + if env3[:result] + # Check if the network is bridged + b4.use Call, IsBridged do |env4, b5| + # if it's not, delete port forwardings. + b5.use UnmapPortForwardings unless env4[:bridged_network] + end + b4.use PowerOffVApp + b4.use DestroyVApp + else + b4.use DestroyVM end - b3.use PowerOffVApp - b3.use DestroyVApp - else - b3.use DestroyVM end end else @@ -140,7 +147,21 @@ def self.action_provision def self.action_read_ssh_info Vagrant::Action::Builder.new.tap do |b| b.use ConnectVCloud - b.use ReadSSHInfo + b.use ReadSSHInfo, 22 + end + end + + def self.action_read_winrm_info + Vagrant::Action::Builder.new.tap do |b| + b.use ConnectVCloud + b.use ReadSSHInfo, 5985 + end + end + + def self.action_read_rdp_info + Vagrant::Action::Builder.new.tap do |b| + b.use ConnectVCloud + b.use ReadSSHInfo, 3389 end end diff --git a/lib/vagrant-vcloud/action/build_vapp.rb b/lib/vagrant-vcloud/action/build_vapp.rb index 7d5be8d..dec7683 100644 --- a/lib/vagrant-vcloud/action/build_vapp.rb +++ b/lib/vagrant-vcloud/action/build_vapp.rb @@ -112,9 +112,7 @@ def call(env) env[:ui].info('Building vApp...') vapp_prefix = cfg.vapp_prefix - if vapp_prefix.nil? - vapp_prefix = "Vagrant" - end + vapp_prefix = 'Vagrant' if vapp_prefix.nil? compose = cnx.compose_vapp_from_vm( cfg.vdc_id, @@ -124,7 +122,7 @@ def call(env) "#{Socket.gethostname.downcase} using vagrant-vcloud on " + "#{Time.now.strftime("%B %d, %Y")}", { - vm_name => cfg.catalog_item[:vms_hash][env[:machine].box.name.to_s][:id] + vm_name => cfg.catalog_item[:vms_hash].first.last[:id] }, network_options ) @@ -180,7 +178,7 @@ def call(env) recompose = cnx.recompose_vapp_from_vm( env[:machine].get_vapp_id, { - vm_name => cfg.catalog_item[:vms_hash][env[:machine].box.name.to_s][:id] + vm_name => cfg.catalog_item[:vms_hash].first.last[:id] }, network_options ) diff --git a/lib/vagrant-vcloud/action/forward_ports.rb b/lib/vagrant-vcloud/action/forward_ports.rb index de74f47..4713234 100644 --- a/lib/vagrant-vcloud/action/forward_ports.rb +++ b/lib/vagrant-vcloud/action/forward_ports.rb @@ -59,8 +59,6 @@ def forward_ports :nat_protocol => fp.protocol.upcase, :vapp_scoped_local_id => vm_info[:vapp_scoped_local_id] } - - edge_ports << fp.host_port end if !ports.empty? @@ -91,27 +89,41 @@ def forward_ports cfg.vdc_edge_gateway && \ cfg.network_bridge.nil? - - edge_ports.each do |port| - @env[:ui].info( - "Creating NAT rules on [#{cfg.vdc_edge_gateway}] " + - "for IP [#{cfg.vdc_edge_gateway_ip}] port #{port}." - ) + vapp_edge_ip = cnx.get_vapp_edge_public_ip(vapp_id) + @logger.debug('Getting edge gateway port forwarding rules...') + edge_gateway_rules = cnx.get_edge_gateway_rules(cfg.vdc_edge_gateway, + cfg.vdc_id) + vapp_edge_dnat_rules = edge_gateway_rules.select {|r| (r[:rule_type] == 'DNAT' && + r[:translated_ip] == vapp_edge_ip)} + vapp_edge_ports_in_use = vapp_edge_dnat_rules.map{|r| r[:original_port].to_i}.to_set + + ports.each do |port| + if port[:vapp_scoped_local_id] == vm_info[:vapp_scoped_local_id] && + !vapp_edge_ports_in_use.include?(port[:nat_external_port]) + @env[:ui].info( + "Creating NAT rules on [#{cfg.vdc_edge_gateway}] " + + "for IP [#{vapp_edge_ip}] port #{port[:nat_external_port]}." + ) + + edge_ports << port[:nat_external_port] + end end - # Add the vShield Edge Gateway rules - add_ports = cnx.add_edge_gateway_rules( - cfg.vdc_edge_gateway, - cfg.vdc_id, - cfg.vdc_edge_gateway_ip, - vapp_id, - edge_ports - ) + if !edge_ports.empty? + # Add the vShield Edge Gateway rules + add_ports = cnx.add_edge_gateway_rules( + cfg.vdc_edge_gateway, + cfg.vdc_id, + cfg.vdc_edge_gateway_ip, + vapp_id, + edge_ports + ) - wait = cnx.wait_task_completion(add_ports) + wait = cnx.wait_task_completion(add_ports) - if !wait[:errormsg].nil? - raise Errors::ComposeVAppError, :message => wait[:errormsg] + if !wait[:errormsg].nil? + raise Errors::ComposeVAppError, :message => wait[:errormsg] + end end end diff --git a/lib/vagrant-vcloud/action/handle_nat_port_collisions.rb b/lib/vagrant-vcloud/action/handle_nat_port_collisions.rb index bc1867f..bf9d7f3 100644 --- a/lib/vagrant-vcloud/action/handle_nat_port_collisions.rb +++ b/lib/vagrant-vcloud/action/handle_nat_port_collisions.rb @@ -47,10 +47,12 @@ def call(env) vm = cnx.get_vapp(vapp_id) vm_info = vm[:vms_hash][vm_name.to_sym] + vapp_edge_ip = cnx.get_vapp_edge_public_ip(vapp_id) + @logger.debug('Getting edge gateway port forwarding rules...') edge_gateway_rules = cnx.get_edge_gateway_rules(cfg.vdc_edge_gateway, cfg.vdc_id) - edge_dnat_rules = edge_gateway_rules.select {|r| (r[:rule_type] == 'DNAT')} + edge_dnat_rules = edge_gateway_rules.select {|r| (r[:rule_type] == 'DNAT' && r[:translated_ip] != vapp_edge_ip)} edge_ports_in_use = edge_dnat_rules.map{|r| r[:original_port].to_i}.to_set @logger.debug('Getting port forwarding rules...') @@ -65,16 +67,6 @@ def call(env) guest_port = options[:guest] host_port = options[:host] - # Find if there already is a DNAT rule to this vApp - if r = edge_dnat_rules.find { |rule| (rule[:translated_ip] == vm_info[:ip] && - rule[:translated_port] == guest_port.to_s) } - - @logger.info( - "Found existing edge gateway port forwarding rule #r[:original_port] to #{guest_port}" - ) - options[:already_exists_on_edge] = true - end - # Find if there already is a NAT rule to guest_port of this VM if r = vapp_nat_rules.find { |rule| (rule[:vapp_scoped_local_id] == vm_info[:vapp_scoped_local_id] && rule[:nat_internal_port] == guest_port.to_s) } diff --git a/lib/vagrant-vcloud/action/inventory_check.rb b/lib/vagrant-vcloud/action/inventory_check.rb index 7ce3464..8e7c0e8 100644 --- a/lib/vagrant-vcloud/action/inventory_check.rb +++ b/lib/vagrant-vcloud/action/inventory_check.rb @@ -20,7 +20,14 @@ def vcloud_upload_box(env) cnx = cfg.vcloud_cnx.driver box_dir = env[:machine].box.directory.to_s - box_file = env[:machine].box.name.to_s + + if env[:machine].box.name.to_s.include? '/' + box_file = env[:machine].box.name.rpartition('/').last.to_s + box_name = env[:machine].box.name.to_s + else + box_file = env[:machine].box.name.to_s + box_name = box_file + end box_ovf = "#{box_dir}/#{box_file}.ovf" @@ -29,7 +36,7 @@ def vcloud_upload_box(env) @logger.debug("OVF File: #{box_ovf}") upload_ovf = cnx.upload_ovf( cfg.vdc_id, - env[:machine].box.name.to_s, + box_name, 'Vagrant Box', box_ovf, cfg.catalog_id, @@ -40,7 +47,7 @@ def vcloud_upload_box(env) ) env[:ui].info( - "Adding [#{env[:machine].box.name.to_s}] to " + + "Adding [#{box_name}] to " + "Catalog [#{cfg.catalog_name}]" ) add_ovf_to_catalog = cnx.wait_task_completion(upload_ovf) @@ -53,7 +60,7 @@ def vcloud_upload_box(env) # Retrieve catalog_item ID cfg.catalog_item = cnx.get_catalog_item_by_name( cfg.catalog_id, - env[:machine].box.name.to_s + box_name ) end @@ -82,6 +89,14 @@ def vcloud_check_inventory(env) cfg = env[:machine].provider_config cnx = cfg.vcloud_cnx.driver + if env[:machine].box.name.to_s.include? '/' + box_file = env[:machine].box.name.rpartition('/').last.to_s + box_name = env[:machine].box.name.to_s + else + box_file = env[:machine].box.name.to_s + box_name = box_file + end + cfg.org = cnx.get_organization_by_name(cfg.org_name) cfg.org_id = cnx.get_organization_id_by_name(cfg.org_name) @@ -113,11 +128,11 @@ def vcloud_check_inventory(env) @logger.debug( "Getting catalog item with cfg.catalog_id: [#{cfg.catalog_id}] " + - "and machine name [#{env[:machine].box.name.to_s}]" + "and machine name [#{box_name}]" ) cfg.catalog_item = cnx.get_catalog_item_by_name( cfg.catalog_id, - env[:machine].box.name.to_s + box_name ) @logger.debug("Catalog item is now #{cfg.catalog_item}") @@ -148,18 +163,18 @@ def vcloud_check_inventory(env) if !cfg.catalog_item env[:ui].warn( - "Catalog item [#{env[:machine].box.name.to_s}] " + + "Catalog item [#{box_name}] " + "in Catalog [#{cfg.catalog_name}] does not exist!" ) user_input = env[:ui].ask( - "Would you like to upload the [#{env[:machine].box.name.to_s}] " + + "Would you like to upload the [#{box_name}] " + "box to [#{cfg.catalog_name}] Catalog?\n" + 'Choice (yes/no): ' ) if user_input.downcase == 'yes' || user_input.downcase == 'y' - env[:ui].info("Uploading [#{env[:machine].box.name.to_s}]...") + env[:ui].info("Uploading [#{box_name}]...") vcloud_upload_box(env) else env[:ui].error('Catalog item not available, exiting...') @@ -171,7 +186,7 @@ def vcloud_check_inventory(env) else @logger.info( - "Using catalog item [#{env[:machine].box.name.to_s}] " + + "Using catalog item [#{box_name}] " + "in Catalog [#{cfg.catalog_name}]..." ) end diff --git a/lib/vagrant-vcloud/action/message_not_created.rb b/lib/vagrant-vcloud/action/message_not_created.rb index 239a8e9..dbf1d80 100644 --- a/lib/vagrant-vcloud/action/message_not_created.rb +++ b/lib/vagrant-vcloud/action/message_not_created.rb @@ -8,7 +8,7 @@ def initialize(app, env) def call(env) # FIXME: this error should be categorized - env[:ui].info(I18n.t('vcloud.vm_not_created')) + env[:ui].info(I18n.t('vagrant_vcloud.vm_not_created')) @app.call(env) end end diff --git a/lib/vagrant-vcloud/action/power_on.rb b/lib/vagrant-vcloud/action/power_on.rb index f30a333..ff403de 100644 --- a/lib/vagrant-vcloud/action/power_on.rb +++ b/lib/vagrant-vcloud/action/power_on.rb @@ -13,8 +13,22 @@ def call(env) cfg = env[:machine].provider_config cnx = cfg.vcloud_cnx.driver + env[:ui].info('Setting VM hardware...') + + set_vm_hardware = cnx.set_vm_hardware(env[:machine].id, cfg) + if set_vm_hardware + cnx.wait_task_completion(set_vm_hardware) + end + env[:ui].info('Powering on VM...') + if ! cfg.nested_hypervisor.nil? + set_vm_nested_hypervisor = cnx.set_vm_nested_hypervisor(env[:machine].id, cfg.nested_hypervisor) + if set_vm_nested_hypervisor + cnx.wait_task_completion(set_vm_nested_hypervisor) + end + end + poweron_vm = cnx.poweron_vm(env[:machine].id) cnx.wait_task_completion(poweron_vm) diff --git a/lib/vagrant-vcloud/action/read_ssh_info.rb b/lib/vagrant-vcloud/action/read_ssh_info.rb index 1a9f74a..62cf65e 100644 --- a/lib/vagrant-vcloud/action/read_ssh_info.rb +++ b/lib/vagrant-vcloud/action/read_ssh_info.rb @@ -2,8 +2,9 @@ module VagrantPlugins module VCloud module Action class ReadSSHInfo - def initialize(app, env) + def initialize(app, env, port = 22) @app = app + @port = port @logger = Log4r::Logger.new('vagrant_vcloud::action::read_ssh_info') end @@ -15,21 +16,21 @@ def call(env) # Small method to check the tcp connection to an ip:port works. # Return false if anything fails, and true if it succeeded. - def check_for_ssh(ip, port) + def check_for_port(ip, port, port_name) begin Timeout::timeout(1) do begin s = TCPSocket.new(ip, port) s.close - @logger.debug("SSH Connection successful !") + @logger.debug("#{port_name} Connection successful !") return true - rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH - @logger.debug("SSH Connection Refused/Host Unreachable...") + rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EHOSTDOWN + @logger.debug("#{port_name} Connection Refused/Host Unreachable...") return false end end rescue Timeout::Error - @logger.debug("SSH Connection Timeout...") + @logger.debug("#{port_name} Connection Timeout...") end return false @@ -68,14 +69,14 @@ def read_ssh_info(env) ) @external_ip = vm_info[:networks]['Vagrant-vApp-Net'][:ip] - @external_port = '22' + @external_port = "#{@port}" else @logger.debug('Getting port forwarding rules...') rules = cnx.get_vapp_port_forwarding_rules(vapp_id) rules.each do |rule| - if rule[:vapp_scoped_local_id] == myhash[:vapp_scoped_local_id] && rule[:nat_internal_port] == '22' + if rule[:vapp_scoped_local_id] == myhash[:vapp_scoped_local_id] && rule[:nat_internal_port] == "#{@port}" @external_ip = rule[:nat_external_ip] @external_port = rule[:nat_external_port] break @@ -87,12 +88,33 @@ def read_ssh_info(env) @logger.debug( "We're running vagrant behind an Organization vDC Edge" ) - @external_ip = cfg.vdc_edge_gateway_ip + + # + # Add config.ssh.host support + # http://docs.vagrantup.com/v2/vagrantfile/ssh_settings.html + # + if env[:machine].config.ssh.host + @logger.debug( + 'SSH Host setting configured too: ' \ + "#{env[:machine].config.ssh.host}" + ) + @external_ip = env[:machine].config.ssh.host + else + @logger.debug( + "Using Edge Gateway IP: #{cfg.vdc_edge_gateway_ip}" + ) + @external_ip = cfg.vdc_edge_gateway_ip + end end end + port_name = "SSH" + if @port == 5985 + port_name = "WinRM" + end + @logger.debug( - "SSH INFO: IP #{@external_ip} and Port #{@external_port}" + "#{port_name} INFO: IP #{@external_ip} and Port #{@external_port}" ) # tsugliani: Temporary Fix for Issue #56 @@ -104,17 +126,19 @@ def read_ssh_info(env) # and something like: # # retryable(:on => Vagrant::Errors::SSHSomething, :tries => 10, :sleep => 5) do - # check_for_ssh(ip, port, :error_class => Vagrant::Errors::SSHSomething) + # check_for_port(ip, port, "SSH", :error_class => Vagrant::Errors::SSHSomething) # end # sleep_counter = 5 - while check_for_ssh(@external_ip, @external_port) == false - env[:ui].info( - "Waiting for SSH Access on #{@external_ip}:#{@external_port} ... " - ) - sleep sleep_counter - sleep_counter += 1 + if @port == 22 || @port == 5985 + while check_for_port(@external_ip, @external_port, port_name) == false + env[:ui].info( + "Waiting for #{port_name} Access on #{@external_ip}:#{@external_port} ... " + ) + sleep sleep_counter + sleep_counter += 1 + end end # If we are here, then SSH is ready, continue diff --git a/lib/vagrant-vcloud/action/unmap_port_forwardings.rb b/lib/vagrant-vcloud/action/unmap_port_forwardings.rb index a4e37a6..65bffff 100644 --- a/lib/vagrant-vcloud/action/unmap_port_forwardings.rb +++ b/lib/vagrant-vcloud/action/unmap_port_forwardings.rb @@ -39,30 +39,31 @@ def call(env) @logger.debug('Getting vApp information...') vm = cnx.get_vapp(vapp_id) myhash = vm[:vms_hash][vm_name.to_sym] - @logger.debug('Getting port forwarding rules...') rules = cnx.get_vapp_port_forwarding_rules(vapp_id) - # FIXME: not familiar with this syntax (tsugliani) - new_rule_set = rules.select { - |h| !myhash[:vapp_scoped_local_id].include? h[:vapp_scoped_local_id] - } + unless myhash.nil? + # FIXME: not familiar with this syntax (tsugliani) + new_rule_set = rules.select { + |h| !myhash[:vapp_scoped_local_id].include? h[:vapp_scoped_local_id] + } - @logger.debug("OUR NEW RULE SET, PURGED: #{new_rule_set}") + @logger.debug("OUR NEW RULE SET, PURGED: #{new_rule_set}") - remove_ports = cnx.set_vapp_port_forwarding_rules( - vapp_id, - 'Vagrant-vApp-Net', - :fence_mode => 'natRouted', - :parent_network => cfg.vdc_network_id, - :nat_policy_type => 'allowTraffic', - :nat_rules => new_rule_set - ) + remove_ports = cnx.set_vapp_port_forwarding_rules( + vapp_id, + 'Vagrant-vApp-Net', + :fence_mode => 'natRouted', + :parent_network => cfg.vdc_network_id, + :nat_policy_type => 'allowTraffic', + :nat_rules => new_rule_set + ) - wait = cnx.wait_task_completion(remove_ports) + wait = cnx.wait_task_completion(remove_ports) - unless wait[:errormsg].nil? - fail Errors::ComposeVAppError, :message => wait[:errormsg] + unless wait[:errormsg].nil? + fail Errors::ComposeVAppError, :message => wait[:errormsg] + end end @app.call(env) diff --git a/lib/vagrant-vcloud/cap/forwarded_ports.rb b/lib/vagrant-vcloud/cap/forwarded_ports.rb index 2636923..dd3eee1 100644 --- a/lib/vagrant-vcloud/cap/forwarded_ports.rb +++ b/lib/vagrant-vcloud/cap/forwarded_ports.rb @@ -2,7 +2,6 @@ module VagrantPlugins module VCloud module Cap module ForwardedPorts - # Reads the forwarded ports that currently exist on the machine # itself. This raises an exception if the machine isn't running. # @@ -20,9 +19,7 @@ def self.forwarded_ports(machine) vm = cnx.get_vapp(vapp_id) myhash = vm[:vms_hash][vm_name.to_sym] - if vm.nil? - return - end + return if vm.nil? if cfg.network_bridge.nil? rules = cnx.get_vapp_port_forwarding_rules(vapp_id) @@ -35,9 +32,7 @@ def self.forwarded_ports(machine) end result end - end end end end - diff --git a/lib/vagrant-vcloud/cap/public_address.rb b/lib/vagrant-vcloud/cap/public_address.rb index 5f599d3..eba2cb4 100644 --- a/lib/vagrant-vcloud/cap/public_address.rb +++ b/lib/vagrant-vcloud/cap/public_address.rb @@ -2,9 +2,7 @@ module VagrantPlugins module VCloud module Cap module PublicAddress - def self.public_address(machine) - # Initial try for vagrant share feature. # It seems ssh_info[:port] is given automatically. # I think this feature was built planning that the port forwarding diff --git a/lib/vagrant-vcloud/cap/rdp_info.rb b/lib/vagrant-vcloud/cap/rdp_info.rb new file mode 100644 index 0000000..8b2d587 --- /dev/null +++ b/lib/vagrant-vcloud/cap/rdp_info.rb @@ -0,0 +1,18 @@ +module VagrantPlugins + module VCloud + module Cap + module RDP + + # Reads the RDP forwarded port that currently exists on the machine + # itself. This raises an exception if the machine isn't running. + # @return [Hash] Host => Guest port mappings. + def self.rdp_info(machine) + env = machine.action('read_rdp_info') + env[:machine_ssh_info] + end + + end + end + end +end + diff --git a/lib/vagrant-vcloud/cap/winrm_info.rb b/lib/vagrant-vcloud/cap/winrm_info.rb new file mode 100644 index 0000000..e8442c2 --- /dev/null +++ b/lib/vagrant-vcloud/cap/winrm_info.rb @@ -0,0 +1,18 @@ +module VagrantPlugins + module VCloud + module Cap + module WinRM + + # Reads the WinRM forwarded port that currently exists on the machine + # itself. This raises an exception if the machine isn't running. + # @return [Hash] Host => Guest port mappings. + def self.winrm_info(machine) + env = machine.action('read_winrm_info') + env[:machine_ssh_info] + end + + end + end + end +end + diff --git a/lib/vagrant-vcloud/command.rb b/lib/vagrant-vcloud/command.rb index a0f2b76..2be123d 100644 --- a/lib/vagrant-vcloud/command.rb +++ b/lib/vagrant-vcloud/command.rb @@ -44,7 +44,7 @@ def command_vcloud_status(cfg, vapp_id) puts table end - def command_vcloud_network(cfg, vapp_id) + def command_vcloud_network(cfg, vapp_id, ssh_host) # FIXME: this needs to be fixed to accomodate the bridged scenario # potentially showing only the assigned IPs in the VMs @@ -97,10 +97,23 @@ def command_vcloud_network(cfg, vapp_id) # If rules don't match, you will not see them ! if edge_gateway_rule # DNAT rule from edge to vapp to vm + connect_host = nil + + # Add support for config.ssh.host + if ssh_host + connect_host = "#{ssh_host}:" + + "#{vapp_edge_rule[:nat_external_port]}" + + ' -> ' + + "#{cfg.vdc_edge_gateway_ip}:" + + "#{vapp_edge_rule[:nat_external_port]}" + else + connect_host = "#{cfg.vdc_edge_gateway_ip}:" + + "#{vapp_edge_rule[:nat_external_port]}" + end + network_table << [ "#{vm[0]}", - "#{cfg.vdc_edge_gateway_ip}:" + - "#{vapp_edge_rule[:nat_external_port]}" + + "#{connect_host}" + " -> #{vapp_edge_ip}:" + "#{vapp_edge_rule[:nat_external_port]}" + " -> #{vm[1][:addresses][0]}:" + @@ -223,6 +236,7 @@ def execute puts 'Initializing vCloud Director provider...' # initialize some variables + ssh_host = nil vapp_id = nil cfg = nil @@ -246,6 +260,7 @@ def execute # populate cfg & vApp Id for later use. cfg = machine.provider_config vapp_id = machine.get_vapp_id + ssh_host = machine.config.ssh.host break end @@ -255,7 +270,7 @@ def execute when :status command_vcloud_status(cfg, vapp_id) when :network - command_vcloud_network(cfg, vapp_id) + command_vcloud_network(cfg, vapp_id, ssh_host) when :redeploy_edge_gw command_vcloud_redeploy_edge_gw(cfg) end diff --git a/lib/vagrant-vcloud/config.rb b/lib/vagrant-vcloud/config.rb index 75d80f5..fefe1d6 100644 --- a/lib/vagrant-vcloud/config.rb +++ b/lib/vagrant-vcloud/config.rb @@ -133,6 +133,15 @@ class Config < Vagrant.plugin('2', :config) # vApp Id (String) attr_accessor :vAppId + # VM memory size in MB (Integer) + attr_accessor :memory + + # VM number of cpus (Integer) + attr_accessor :cpus + + # NestedHypervisor (Bool) + attr_accessor :nested_hypervisor + def validate(machine) errors = _detected_errors diff --git a/lib/vagrant-vcloud/driver/base.rb b/lib/vagrant-vcloud/driver/base.rb index 3f141eb..8255be6 100644 --- a/lib/vagrant-vcloud/driver/base.rb +++ b/lib/vagrant-vcloud/driver/base.rb @@ -246,6 +246,9 @@ def upload_ovf(vdc_id, vapp_name, vapp_description, ovf_file, catalog_id, upload_options = {}) end + def set_vm_hardware(vm_id, cfg) + end + ## # Fetch information for a given task def get_task(task_id) @@ -492,4 +495,4 @@ def convert_vapp_status(status_code) end # class end end -end \ No newline at end of file +end diff --git a/lib/vagrant-vcloud/driver/version_5_1.rb b/lib/vagrant-vcloud/driver/version_5_1.rb index 1a3ed8b..b497bee 100644 --- a/lib/vagrant-vcloud/driver/version_5_1.rb +++ b/lib/vagrant-vcloud/driver/version_5_1.rb @@ -1838,6 +1838,74 @@ def set_vm_guest_customization(vm_id, computer_name, config = {}) task_id end + # Enable VM Nested Hardware-Assisted Virtualization + def set_vm_nested_hypervisor(vm_id, enable) + action = enable ? "enable" : "disable" + params = { + 'method' => :post, + 'command' => "/vApp/vm-#{vm_id}/action/#{action}NestedHypervisor" + } + + _response, headers = send_request(params) + task_id = headers['Location'].gsub("#{@api_url}/task/", '') + task_id + end + + ## + # Set memory and number of cpus in virtualHardwareSection of a given vm + # returns task_id or nil if there is no task to wait for + def set_vm_hardware(vm_id, cfg) + params = { + 'method' => :get, + 'command' => "/vApp/vm-#{vm_id}/virtualHardwareSection" + } + + changed = false + response, _headers = send_request(params) + + response.css('ovf|Item').each do |item| + type = item.css('rasd|ResourceType').first + if type.content == '3' + # cpus + if cfg.cpus + if item.at_css('rasd|VirtualQuantity').content != cfg.cpus.to_s + item.at_css('rasd|VirtualQuantity').content = cfg.cpus + item.at_css('rasd|ElementName').content = "#{cfg.cpus} virtual CPU(s)" + changed = true + end + end + elsif type.content == '4' + # memory + if cfg.memory + if item.at_css('rasd|VirtualQuantity').content != cfg.memory.to_s + item.at_css('rasd|VirtualQuantity').content = cfg.memory + item.at_css('rasd|ElementName').content = "#{cfg.memory} MB of memory" + changed = true + end + end + end + end + + if changed + params = { + 'method' => :put, + 'command' => "/vApp/vm-#{vm_id}/virtualHardwareSection" + } + + _response, headers = send_request( + params, + response.to_xml, + 'application/vnd.vmware.vcloud.virtualhardwaresection+xml' + ) + + task_id = headers['Location'].gsub("#{@api_url}/task/", '') + task_id + else + return nil + end + end + + ## # Fetch details about a given VM def get_vm(vm_id) diff --git a/lib/vagrant-vcloud/plugin.rb b/lib/vagrant-vcloud/plugin.rb index da9947a..d4bfb51 100644 --- a/lib/vagrant-vcloud/plugin.rb +++ b/lib/vagrant-vcloud/plugin.rb @@ -41,6 +41,16 @@ class Plugin < Vagrant.plugin('2') Cap::ForwardedPorts end + provider_capability(:vcloud, :winrm_info) do + require_relative "cap/winrm_info" + Cap::WinRM + end + + provider_capability(:vcloud, :rdp_info) do + require_relative "cap/rdp_info" + Cap::RDP + end + # Added a vagrant vcloud-status command to enhance troubleshooting and # visibility. command('vcloud') do diff --git a/lib/vagrant-vcloud/version.rb b/lib/vagrant-vcloud/version.rb index f625bdc..2931b83 100644 --- a/lib/vagrant-vcloud/version.rb +++ b/lib/vagrant-vcloud/version.rb @@ -1,5 +1,5 @@ module VagrantPlugins module VCloud - VERSION = '0.3.3' + VERSION = '0.4.0' end end