vagrant-vcloud 0.4.4 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
File without changes
File without changes
@@ -37,7 +37,7 @@ module VagrantPlugins
37
37
  @username = username
38
38
  @password = password
39
39
  @org_name = org_name
40
- @api_version = '5.1'
40
+ @api_version = '5.5'
41
41
  @id = nil
42
42
 
43
43
  @cached_vapp_edge_public_ips = {}
@@ -571,7 +571,7 @@ module VagrantPlugins
571
571
  end
572
572
 
573
573
  ##
574
- # Shutdown a given VM
574
+ # Poweroff a given VM
575
575
  # Using undeploy as a REAL powerOff
576
576
  # Only poweroff will put the VM into a partially powered off state.
577
577
  def poweroff_vm(vm_id)
@@ -595,6 +595,31 @@ module VagrantPlugins
595
595
  task_id
596
596
  end
597
597
 
598
+ ##
599
+ # Shutdown a given VM
600
+ # Using undeploy with shutdown, without VMware Tools this WILL FAIL.
601
+ #
602
+ def shutdown_vm(vm_id)
603
+ builder = Nokogiri::XML::Builder.new do |xml|
604
+ xml.UndeployVAppParams(
605
+ 'xmlns' => 'http://www.vmware.com/vcloud/v1.5'
606
+ ) { xml.UndeployPowerAction 'shutdown' }
607
+ end
608
+
609
+ params = {
610
+ 'method' => :post,
611
+ 'command' => "/vApp/vm-#{vm_id}/action/undeploy"
612
+ }
613
+
614
+ _response, headers = send_request(
615
+ params,
616
+ builder.to_xml,
617
+ 'application/vnd.vmware.vcloud.undeployVAppParams+xml'
618
+ )
619
+ task_id = URI(headers['Location']).path.gsub('/api/task/', '')
620
+ task_id
621
+ end
622
+
598
623
  ##
599
624
  # Suspend a given VM
600
625
  def suspend_vm(vm_id)
@@ -688,7 +713,7 @@ module VagrantPlugins
688
713
  ).first[:href]).path.gsub('/api/task/', '')
689
714
 
690
715
  catalog_id = URI(response.css(
691
- "AdminCatalog Link [type='application/vnd.vmware.vcloud.catalog+xml']"
716
+ "AdminCatalog Link[type='application/vnd.vmware.vcloud.catalog+xml']"
692
717
  ).first[:href]).path.gsub('/api/catalog/', '')
693
718
 
694
719
  { :task_id => task_id, :catalog_id => catalog_id }
@@ -748,74 +773,120 @@ module VagrantPlugins
748
773
  # - vapp_description: description of the target vapp
749
774
  # - vm_list: hash with IDs of the VMs used in the composing process
750
775
  # - network_config: hash of the network configuration for the vapp
751
- def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list = {}, network_config = {})
776
+ def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list = {}, network_config = [], _cfg)
752
777
  builder = Nokogiri::XML::Builder.new do |xml|
753
- xml.ComposeVAppParams(
754
- 'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
755
- 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
756
- 'name' => vapp_name,
757
- 'deploy' => 'false',
758
- 'powerOn' => 'false') {
778
+ xml.ComposeVAppParams('xmlns' => 'http://www.vmware.com/vcloud/v1.5',
779
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
780
+ 'deploy' => 'false',
781
+ 'powerOn' => 'false',
782
+ 'name' => vapp_name) {
759
783
  xml.Description vapp_description
760
784
  xml.InstantiationParams {
761
785
  xml.NetworkConfigSection {
762
786
  xml['ovf'].Info 'Configuration parameters for logical networks'
763
- xml.NetworkConfig('networkName' => network_config[:name]) {
764
- xml.Configuration {
765
- if network_config[:fence_mode] != 'bridged'
766
- xml.IpScopes {
767
- xml.IpScope {
768
- xml.IsInherited(network_config[:is_inherited] || 'false')
769
- xml.Gateway network_config[:gateway]
770
- xml.Netmask network_config[:netmask]
771
- xml.Dns1 network_config[:dns1] if network_config[:dns1]
772
- xml.Dns2 network_config[:dns2] if network_config[:dns2]
773
- xml.DnsSuffix network_config[:dns_suffix] if network_config[:dns_suffix]
774
- xml.IpRanges {
775
- xml.IpRange {
776
- xml.StartAddress network_config[:start_address]
777
- xml.EndAddress network_config[:end_address]
787
+ network_config.each do |network|
788
+ xml.NetworkConfig('networkName' => network[:name]) {
789
+ xml.Configuration {
790
+ if network[:fence_mode] != 'bridged'
791
+ xml.IpScopes {
792
+ xml.IpScope {
793
+ xml.IsInherited(network[:is_inherited] || 'false')
794
+ xml.Gateway network[:gateway]
795
+ xml.Netmask network[:netmask]
796
+ xml.Dns1 network[:dns1] if network[:dns1]
797
+ xml.Dns2 network[:dns2] if network[:dns2]
798
+ xml.DnsSuffix network[:dns_suffix] if network[:dns_suffix]
799
+ xml.IpRanges {
800
+ xml.IpRange {
801
+ xml.StartAddress network[:start_address]
802
+ xml.EndAddress network[:end_address]
803
+ }
778
804
  }
779
805
  }
780
806
  }
781
- }
782
- end
783
- xml.ParentNetwork("href" => "#{@api_url}/network/#{network_config[:parent_network]}")
784
- xml.FenceMode network_config[:fence_mode]
785
- if network_config[:fence_mode] != 'bridged'
786
- xml.Features {
787
- xml.FirewallService {
788
- xml.IsEnabled(network_config[:enable_firewall] || "false")
789
- }
790
- xml.NatService {
791
- xml.IsEnabled "true"
792
- xml.NatType "portForwarding"
793
- xml.Policy(network_config[:nat_policy_type] || "allowTraffic")
807
+ end
808
+ xml.ParentNetwork("href" => "#{@api_url}/network/#{network[:parent_network]}") if network[:parent_network]
809
+ xml.FenceMode network[:fence_mode]
810
+ if network[:fence_mode] != 'bridged'
811
+ xml.Features {
812
+ if network[:dhcp_enabled] == 'true'
813
+ xml.DhcpService {
814
+ xml.IsEnabled "true"
815
+ xml.DefaultLeaseTime "3600"
816
+ xml.MaxLeaseTime "7200"
817
+ xml.IpRange {
818
+ xml.StartAddress network[:dhcp_start]
819
+ xml.EndAddress network[:dhcp_end]
820
+ }
821
+ }
822
+ end
823
+ xml.FirewallService {
824
+ xml.IsEnabled(network[:enable_firewall] || "false")
825
+ }
826
+ xml.NatService {
827
+ xml.IsEnabled "true"
828
+ xml.NatType "portForwarding"
829
+ xml.Policy(network[:nat_policy_type] || "allowTraffic")
830
+ }
794
831
  }
795
- }
796
- end
832
+ end
833
+ }
797
834
  }
798
- }
835
+ end #networks
799
836
  }
800
837
  }
801
838
  vm_list.each do |vm_name, vm_id|
802
839
  xml.SourcedItem {
803
840
  xml.Source('href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}", 'name' => vm_name)
804
841
  xml.InstantiationParams {
805
- xml.NetworkConnectionSection(
806
- 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
807
- 'type' => 'application/vnd.vmware.vcloud.networkConnectionSection+xml',
808
- 'href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
809
- xml['ovf'].Info 'Network config for sourced item'
810
- xml.PrimaryNetworkConnectionIndex '0'
811
- xml.NetworkConnection('network' => network_config[:name]) {
812
- xml.NetworkConnectionIndex '0'
813
- xml.IsConnected 'true'
814
- xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || 'POOL')
842
+ if _cfg.enable_guest_customization.nil? || _cfg.enable_guest_customization
843
+ xml.GuestCustomizationSection(
844
+ 'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
845
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
846
+ xml['ovf'].Info 'VM Guest Customization configuration'
847
+ xml.Enabled true
848
+ if _cfg.guest_customization_change_sid == true
849
+ xml.ChangeSid true
850
+ if _cfg.guest_customization_join_domain == true
851
+ xml.JoinDomainEnabled true
852
+ xml.DomainName _cfg.guest_customization_domain_name
853
+ xml.DomainUserName _cfg.guest_customization_domain_user_name
854
+ xml.DomainUserPassword _cfg.guest_customization_domain_user_password
855
+ xml.MachineObjectOU _cfg.guest_customization_domain_ou if !_cfg.guest_customization_domain_ou.nil?
856
+ end
857
+ end
858
+ if _cfg.guest_customization_admin_password_enabled
859
+ xml.AdminPasswordEnabled true
860
+ xml.AdminPasswordAuto true if _cfg.guest_customization_admin_password_auto
861
+ xml.AdminPassword _cfg.guest_customization_admin_password if !_cfg.guest_customization_admin_password.nil?
862
+ if _cfg.guest_customization_admin_auto_login == true
863
+ xml.AdminAutoLogonEnabled true
864
+ xml.AdminAutoLogonCount _cfg.guest_customization_admin_auto_login_count
865
+ end
866
+ else
867
+ xml.AdminPasswordEnabled false
868
+ end
869
+ xml.ResetPasswordRequired _cfg.guest_customization_admin_password_reset if !_cfg.guest_customization_admin_password_reset.nil?
870
+ xml.CustomizationScript{ xml.cdata(_cfg.guest_customization_script) } if !_cfg.guest_customization_script.nil?
871
+ xml.ComputerName vm_name
815
872
  }
816
- }
873
+ end
874
+ if _cfg.nics.nil? && network_config.length == 1
875
+ xml.NetworkConnectionSection(
876
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
877
+ 'type' => 'application/vnd.vmware.vcloud.networkConnectionSection+xml',
878
+ 'href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
879
+ xml['ovf'].Info 'Network config for sourced item'
880
+ xml.PrimaryNetworkConnectionIndex '0'
881
+ xml.NetworkConnection('network' => network_config[0][:name]) {
882
+ xml.NetworkConnectionIndex '0'
883
+ xml.IsConnected 'true'
884
+ xml.IpAddressAllocationMode(network_config[0][:ip_allocation_mode] || 'POOL')
885
+ }
886
+ }
887
+ end
817
888
  }
818
- xml.NetworkAssignment('containerNetwork' => network_config[:name], 'innerNetwork' => network_config[:name])
889
+ xml.NetworkAssignment('containerNetwork' => network_config[0][:name], 'innerNetwork' => network_config[0][:name]) if _cfg.nics.nil? && network_config.length == 1
819
890
  }
820
891
  end
821
892
  xml.AllEULAsAccepted 'true'
@@ -851,7 +922,7 @@ module VagrantPlugins
851
922
  # - vm_list: hash with IDs of the VMs to be used in the composing process
852
923
  # - network_config: hash of the network configuration for the vapp
853
924
 
854
- def recompose_vapp_from_vm(vapp_id, vm_list = {}, network_config = {})
925
+ def recompose_vapp_from_vm(vapp_id, vm_list = {}, network_config = [], _cfg)
855
926
  original_vapp = get_vapp(vapp_id)
856
927
 
857
928
  builder = Nokogiri::XML::Builder.new do |xml|
@@ -862,23 +933,57 @@ module VagrantPlugins
862
933
  xml.Description original_vapp[:description]
863
934
  xml.InstantiationParams {}
864
935
  vm_list.each do |vm_name, vm_id|
865
- xml.SourcedItem {
866
- xml.Source('href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}", 'name' => vm_name)
867
- xml.InstantiationParams {
868
- xml.NetworkConnectionSection(
869
- 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
870
- 'type' => 'application/vnd.vmware.vcloud.networkConnectionSection+xml',
871
- 'href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
872
- xml['ovf'].Info 'Network config for sourced item'
873
- xml.PrimaryNetworkConnectionIndex '0'
874
- xml.NetworkConnection('network' => network_config[:name]) {
875
- xml.NetworkConnectionIndex '0'
876
- xml.IsConnected 'true'
877
- xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || 'POOL')
878
- }
936
+ xml.SourcedItem {
937
+ xml.Source('href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}", 'name' => vm_name)
938
+ xml.InstantiationParams {
939
+ if _cfg.enable_guest_customization.nil? || _cfg.enable_guest_customization
940
+ xml.GuestCustomizationSection(
941
+ 'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
942
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
943
+ xml['ovf'].Info 'VM Guest Customization configuration'
944
+ xml.Enabled true
945
+ if _cfg.guest_customization_change_sid == true
946
+ xml.ChangeSid true
947
+ if _cfg.guest_customization_join_domain == true
948
+ xml.JoinDomainEnabled true
949
+ xml.DomainName _cfg.guest_customization_domain_name
950
+ xml.DomainUserName _cfg.guest_customization_domain_user_name
951
+ xml.DomainUserPassword _cfg.guest_customization_domain_user_password
952
+ xml.MachineObjectOU _cfg.guest_customization_domain_ou if !_cfg.guest_customization_domain_ou.nil?
953
+ end
954
+ end
955
+ if _cfg.guest_customization_admin_password_enabled
956
+ xml.AdminPasswordEnabled true
957
+ xml.AdminPasswordAuto true if _cfg.guest_customization_admin_password_auto
958
+ xml.AdminPassword _cfg.guest_customization_admin_password if !_cfg.guest_customization_admin_password.nil?
959
+ if _cfg.guest_customization_admin_auto_login == true
960
+ xml.AdminAutoLogonEnabled true
961
+ xml.AdminAutoLogonCount _cfg.guest_customization_admin_auto_login_count
962
+ end
963
+ else
964
+ xml.AdminPasswordEnabled false
965
+ end
966
+ xml.ResetPasswordRequired _cfg.guest_customization_admin_password_reset if !_cfg.guest_customization_admin_password_reset.nil?
967
+ xml.CustomizationScript{ xml.cdata(_cfg.guest_customization_script) } if !_cfg.guest_customization_script.nil?
968
+ xml.ComputerName vm_name
969
+ }
970
+ end
971
+ if _cfg.nics.nil? && network_config.length == 1
972
+ xml.NetworkConnectionSection(
973
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1',
974
+ 'type' => 'application/vnd.vmware.vcloud.networkConnectionSection+xml',
975
+ 'href' => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
976
+ xml['ovf'].Info 'Network config for sourced item'
977
+ xml.PrimaryNetworkConnectionIndex '0'
978
+ xml.NetworkConnection('network' => network_config[0][:name]) {
979
+ xml.NetworkConnectionIndex '0'
980
+ xml.IsConnected 'true'
981
+ xml.IpAddressAllocationMode(network_config[0][:ip_allocation_mode] || 'POOL')
982
+ }
983
+ }
984
+ end
879
985
  }
880
- }
881
- xml.NetworkAssignment('containerNetwork' => network_config[:name], 'innerNetwork' => network_config[:name])
986
+ xml.NetworkAssignment('containerNetwork' => network_config[0][:name], 'innerNetwork' => network_config[0][:name]) if _cfg.nics.nil? && network_config.length == 1
882
987
  }
883
988
  end
884
989
  xml.AllEULAsAccepted 'true'
@@ -1011,42 +1116,27 @@ module VagrantPlugins
1011
1116
  # :vm_scoped_local_id => value[:vapp_scoped_local_id]
1012
1117
  # }
1013
1118
 
1014
- def add_vapp_port_forwarding_rules(vapp_id, network_name, config = {})
1015
- builder = Nokogiri::XML::Builder.new do |xml|
1016
- xml.NetworkConfigSection(
1017
- 'xmlns' => 'http://www.vmware.com/vcloud/v1.5',
1018
- 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') {
1019
- xml['ovf'].Info 'Network configuration'
1020
- xml.NetworkConfig('networkName' => network_name) {
1021
- xml.Configuration {
1022
- xml.ParentNetwork('href' => "#{@api_url}/network/#{config[:parent_network]}")
1023
- xml.FenceMode(config[:fence_mode] || 'isolated')
1024
- xml.Features {
1025
- xml.NatService {
1026
- xml.IsEnabled 'true'
1027
- xml.NatType 'portForwarding'
1028
- xml.Policy(config[:nat_policy_type] || 'allowTraffic')
1029
-
1030
- pre_existing = get_vapp_port_forwarding_rules(vapp_id)
1031
-
1032
- config[:nat_rules].concat(pre_existing)
1033
-
1034
- config[:nat_rules].each do |nat_rule|
1035
- xml.NatRule {
1036
- xml.VmRule {
1037
- xml.ExternalPort nat_rule[:nat_external_port]
1038
- xml.VAppScopedVmId nat_rule[:vapp_scoped_local_id]
1039
- xml.VmNicId(nat_rule[:nat_vmnic_id] || '0')
1040
- xml.InternalPort nat_rule[:nat_internal_port]
1041
- xml.Protocol(nat_rule[:nat_protocol] || 'TCP')
1042
- }
1043
- }
1044
- end
1045
- }
1046
- }
1047
- }
1048
- }
1049
- }
1119
+ def add_vapp_port_forwarding_rules(vapp_id, network_name, edge_network_name, config = {})
1120
+ params = {
1121
+ 'method' => :get,
1122
+ 'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
1123
+ }
1124
+ response, _headers = send_request(params)
1125
+
1126
+ nat_svc = response.css("/NetworkConfigSection/NetworkConfig[networkName='#{network_name}']/Configuration/Features/NatService").first
1127
+
1128
+ config[:nat_rules].each do |nr|
1129
+ nat_svc << (
1130
+ "<NatRule>" +
1131
+ "<VmRule>" +
1132
+ "<ExternalPort>#{nr[:nat_external_port]}</ExternalPort>" +
1133
+ "<VAppScopedVmId>#{nr[:vapp_scoped_local_id]}</VAppScopedVmId>" +
1134
+ "<VmNicId>#{nr[:nat_vmnic_id]}</VmNicId>" +
1135
+ "<InternalPort>#{nr[:nat_internal_port]}</InternalPort>" +
1136
+ "<Protocol>#{nr[:nat_protocol]}</Protocol>" +
1137
+ "</VmRule>" +
1138
+ "</NatRule>"
1139
+ )
1050
1140
  end
1051
1141
 
1052
1142
  params = {
@@ -1056,7 +1146,7 @@ module VagrantPlugins
1056
1146
 
1057
1147
  _response, headers = send_request(
1058
1148
  params,
1059
- builder.to_xml,
1149
+ response.to_xml,
1060
1150
  'application/vnd.vmware.vcloud.networkConfigSection+xml'
1061
1151
  )
1062
1152
 
@@ -1067,7 +1157,7 @@ module VagrantPlugins
1067
1157
  # Get vApp port forwarding rules
1068
1158
  #
1069
1159
  # - vapp_id: id of the vApp
1070
- def get_vapp_port_forwarding_rules(vapp_id)
1160
+ def get_vapp_port_forwarding_rules(vapp_id, network_name=nil)
1071
1161
  params = {
1072
1162
  'method' => :get,
1073
1163
  'command' => "/vApp/vapp-#{vapp_id}/networkConfigSection"
@@ -1077,9 +1167,22 @@ module VagrantPlugins
1077
1167
 
1078
1168
  # FIXME: this will return nil if the vApp uses multiple vApp Networks
1079
1169
  # with Edge devices in natRouted/portForwarding mode.
1080
- config = response.css(
1081
- 'NetworkConfigSection/NetworkConfig/Configuration'
1170
+ nconfig = response.css(
1171
+ 'NetworkConfigSection/NetworkConfig'
1082
1172
  )
1173
+ config = nil
1174
+ if nconfig.size > 1
1175
+ nconfig.each {|c|
1176
+ pn = c.css('/Configuration/ParentNetwork')
1177
+ next if pn.size == 0
1178
+ if pn.first['name'] == network_name
1179
+ config = c.css('/Configuration')
1180
+ break
1181
+ end
1182
+ }
1183
+ else
1184
+ config = nconfig.css('/Configuration')
1185
+ end
1083
1186
  fence_mode = config.css('/FenceMode').text
1084
1187
  nat_type = config.css('/Features/NatService/NatType').text
1085
1188
 
@@ -1494,8 +1597,8 @@ module VagrantPlugins
1494
1597
  # - NatType" is set to "portForwarding
1495
1598
  # This will be required to know how to connect to VMs behind the Edge
1496
1599
  # device.
1497
- def get_vapp_edge_public_ip(vapp_id)
1498
- return @cached_vapp_edge_public_ips[vapp_id] unless @cached_vapp_edge_public_ips[vapp_id].nil?
1600
+ def get_vapp_edge_public_ip(vapp_id, network_name=nil)
1601
+ return @cached_vapp_edge_public_ips["#{vapp_id}#{network_name}"] unless @cached_vapp_edge_public_ips["#{vapp_id}#{network_name}"].nil?
1499
1602
 
1500
1603
  # Check the network configuration section
1501
1604
  params = {
@@ -1507,9 +1610,22 @@ module VagrantPlugins
1507
1610
 
1508
1611
  # FIXME: this will return nil if the vApp uses multiple vApp Networks
1509
1612
  # with Edge devices in natRouted/portForwarding mode.
1510
- config = response.css(
1511
- 'NetworkConfigSection/NetworkConfig/Configuration'
1613
+ nconfig = response.css(
1614
+ 'NetworkConfigSection/NetworkConfig'
1512
1615
  )
1616
+ config = nil
1617
+ if nconfig.size > 1
1618
+ nconfig.each {|c|
1619
+ pn = c.css('/Configuration/ParentNetwork')
1620
+ next if pn.size == 0
1621
+ if pn.first['name'] == network_name
1622
+ config = c.css('/Configuration')
1623
+ break
1624
+ end
1625
+ }
1626
+ else
1627
+ config = nconfig.css('/Configuration')
1628
+ end
1513
1629
 
1514
1630
  fence_mode = config.css('/FenceMode').text
1515
1631
  nat_type = config.css('/Features/NatService/NatType').text
@@ -1530,7 +1646,7 @@ module VagrantPlugins
1530
1646
  if edge_ip == ''
1531
1647
  return nil
1532
1648
  else
1533
- @cached_vapp_edge_public_ips[vapp_id] = edge_ip
1649
+ @cached_vapp_edge_public_ips["#{vapp_id}#{network_name}"] = edge_ip
1534
1650
  return edge_ip
1535
1651
  end
1536
1652
  end
@@ -1582,7 +1698,7 @@ module VagrantPlugins
1582
1698
 
1583
1699
  @logger.debug("Getting vAppTemplate ID: #{vapp_template}")
1584
1700
  descriptor_upload = URI(response.css(
1585
- "Files Link [rel='upload:default']"
1701
+ "Files Link[rel='upload:default']"
1586
1702
  ).first[:href]).path.gsub('/transfer/', '')
1587
1703
  transfer_guid = descriptor_upload.gsub('/descriptor.ovf', '')
1588
1704
 
@@ -1623,7 +1739,7 @@ module VagrantPlugins
1623
1739
  while true
1624
1740
  response, _headers = send_request(params)
1625
1741
  @logger.debug('Request...')
1626
- break unless response.css("Files Link [rel='upload:default']").count == 1
1742
+ break unless response.css("Files Link[rel='upload:default']").count == 1
1627
1743
  sleep 1
1628
1744
  end
1629
1745
 
@@ -1645,7 +1761,7 @@ module VagrantPlugins
1645
1761
  }
1646
1762
  response, _headers = send_request(params)
1647
1763
  response.css(
1648
- "Files File [bytesTransferred='0'] Link [rel='upload:default']"
1764
+ "Files File[bytesTransferred='0'] Link[rel='upload:default']"
1649
1765
  ).each do |file|
1650
1766
  file_name = URI(file[:href]).path.gsub("/transfer/#{transfer_guid}/", '')
1651
1767
  upload_filename = "#{ovf_dir}/#{file_name}"
@@ -1700,7 +1816,7 @@ module VagrantPlugins
1700
1816
 
1701
1817
  # Cancel Task
1702
1818
  cancel_hook = URI(response.css(
1703
- "Tasks Task Link [rel='task:cancel']"
1819
+ "Tasks Task Link[rel='task:cancel']"
1704
1820
  ).first[:href]).path.gsub('/api', '')
1705
1821
 
1706
1822
  params = {
@@ -1838,6 +1954,41 @@ module VagrantPlugins
1838
1954
  task_id
1839
1955
  end
1840
1956
 
1957
+ ##
1958
+ # Set VM Network Connection state
1959
+ def set_vm_network_connected(vm_id)
1960
+ params = {
1961
+ 'method' => :get,
1962
+ 'command' => "/vApp/vm-#{vm_id}/networkConnectionSection"
1963
+ }
1964
+ response, _headers = send_request(params)
1965
+
1966
+ changed = false
1967
+ response.css('NetworkConnection').each do |net|
1968
+ ic = net.css('IsConnected')
1969
+ if ic.text != 'true'
1970
+ ic.first.content = 'true'
1971
+ changed = true
1972
+ end
1973
+ end
1974
+
1975
+ if changed
1976
+ params = {
1977
+ 'method' => :put,
1978
+ 'command' => "/vApp/vm-#{vm_id}/networkConnectionSection"
1979
+ }
1980
+
1981
+ _response, headers = send_request(
1982
+ params,
1983
+ response.to_xml,
1984
+ 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
1985
+ )
1986
+
1987
+ task_id = URI(headers['Location']).path.gsub('/api/task/', '')
1988
+ end
1989
+ task_id
1990
+ end
1991
+
1841
1992
  ##
1842
1993
  # Set VM Guest Customization Config
1843
1994
  def set_vm_guest_customization(vm_id, computer_name, config = {})
@@ -1869,6 +2020,13 @@ module VagrantPlugins
1869
2020
 
1870
2021
  # Enable VM Nested Hardware-Assisted Virtualization
1871
2022
  def set_vm_nested_hypervisor(vm_id, enable)
2023
+ vm = get_vm(vm_id)
2024
+ if enable && vm[:hypervisor_enabled] == 'true'
2025
+ return nil
2026
+ elsif !enable && vm[:hypervisor_enabled] == 'false'
2027
+ return nil
2028
+ end
2029
+
1872
2030
  action = enable ? "enable" : "disable"
1873
2031
  params = {
1874
2032
  'method' => :post,
@@ -1890,10 +2048,19 @@ module VagrantPlugins
1890
2048
  }
1891
2049
 
1892
2050
  changed = false
2051
+ instance_id = -1
2052
+ hdd_address_on_parent = -1
2053
+ hdd_parent_id = nil
2054
+ hdd_bus_type = nil
2055
+ hdd_bus_sub_type = nil
2056
+ hdd_count = 0
2057
+ nic_count = 0
2058
+ nic_address_on_parent = -1
1893
2059
  response, _headers = send_request(params)
1894
2060
 
1895
2061
  response.css('ovf|Item').each do |item|
1896
2062
  type = item.css('rasd|ResourceType').first
2063
+ instance_id = [ instance_id, item.css('rasd|InstanceID').first.text.to_i ].max
1897
2064
  if type.content == '3'
1898
2065
  # cpus
1899
2066
  if cfg.cpus
@@ -1912,7 +2079,127 @@ module VagrantPlugins
1912
2079
  changed = true
1913
2080
  end
1914
2081
  end
1915
- end
2082
+ elsif type.content == '10'
2083
+ # network card
2084
+ nic_address_on_parent = nic_address_on_parent + 1
2085
+ # nic_address_on_parent = [ nic_address_on_parent, item.css('rasd|AddressOnParent').first.text.to_i ].max
2086
+ next if !cfg.nics || nic_count == cfg.nics.length
2087
+ nic = cfg.nics[nic_count]
2088
+
2089
+ orig_mac = item.css('rasd|Address').first.text
2090
+ orig_ip = item.css('rasd|Connection').first['vcloud:ipAddress']
2091
+ orig_address_mode = item.css('rasd|Connection').first['vcloud:ipAddressingMode']
2092
+ orig_primary = item.css('rasd|Connection').first['vcloud:primaryNetworkConnection']
2093
+ orig_network = item.css('rasd|Connection').first.text
2094
+ orig_parent = item.css('rasd|AddressOnParent').first.text
2095
+ # resourceSubType cannot be changed for an existing network card
2096
+
2097
+ if !nic[:mac].nil?
2098
+ changed = true if nic[:mac].upcase != orig_mac.upcase
2099
+ end
2100
+ if !nic[:ip].nil?
2101
+ changed = true if orig_ip.nil? || nic[:ip].upcase != orig_ip.upcase
2102
+ end
2103
+ changed = true if nic[:ip_mode].upcase != orig_address_mode.upcase
2104
+ changed = true if nic[:primary] != orig_primary
2105
+ changed = true if nic[:network].upcase != orig_network.upcase
2106
+ changed = true if nic_address_on_parent != orig_parent
2107
+
2108
+ if changed
2109
+ item.css('rasd|Address').first.content = nic[:mac] if !nic[:mac].nil?
2110
+ item.css('rasd|AddressOnParent').first.content = nic_address_on_parent if nic_address_on_parent != orig_parent
2111
+ conn = item.css('rasd|Connection').first
2112
+ conn.content = nic[:network]
2113
+ if nic[:ip_mode].upcase == 'DHCP'
2114
+ conn['vcloud:ipAddressingMode'] = 'DHCP'
2115
+ elsif nic[:ip_mode].upcase == 'STATIC'
2116
+ conn['vcloud:ipAddressingMode'] = 'MANUAL'
2117
+ conn['vcloud:ipAddress'] = nic[:ip]
2118
+ elsif nic[:ip_mode].upcase == 'POOL'
2119
+ conn['vcloud:ipAddressingMode'] = 'POOL'
2120
+ conn['vcloud:ipAddress'] = nic[:ip] if !nic[:ip].nil?
2121
+ end
2122
+ conn['vcloud:primaryNetworkConnection'] = nic[:primary]
2123
+ end
2124
+ nic_count = nic_count + 1
2125
+ elsif type.content == '17'
2126
+ # hard disk
2127
+ hdd_count = hdd_count + 1
2128
+ if hdd_parent_id.nil?
2129
+ hdd_parent_id = item.css('rasd|Parent').first.text
2130
+ hdd_bus_type = item.css('rasd|HostResource').first[:busType]
2131
+ hdd_bus_sub_type = item.css('rasd|HostResource').first[:busSubType]
2132
+ end
2133
+ if hdd_parent_id == item.css('rasd|Parent').first.text
2134
+ hdd_address_on_parent = [ hdd_address_on_parent, item.css('rasd|AddressOnParent').first.text.to_i ].max
2135
+ end
2136
+ end
2137
+ end
2138
+
2139
+ if cfg.add_hdds
2140
+ changed = true
2141
+ cfg.add_hdds.each do |hdd_size|
2142
+ hdd_address_on_parent = hdd_address_on_parent + 1
2143
+ instance_id = instance_id + 1
2144
+ newhdd = Nokogiri::XML::Builder.new do |xml|
2145
+ xml.root('xmlns:rasd' => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData',
2146
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') do
2147
+ xml['ovf'].Item {
2148
+ xml['rasd'].AddressOnParent(hdd_address_on_parent)
2149
+ xml['rasd'].Description("Hard disk")
2150
+ xml['rasd'].ElementName("Hard disk #{hdd_address_on_parent+1}")
2151
+ xml['rasd'].HostResource()
2152
+ xml['rasd'].InstanceID(instance_id)
2153
+ xml['rasd'].Parent(hdd_parent_id)
2154
+ xml['rasd'].ResourceType(17)
2155
+ }
2156
+ end
2157
+ end
2158
+ hr = newhdd.doc.css('rasd|HostResource').first
2159
+ hr['xmlns:vcloud'] = 'http://www.vmware.com/vcloud/v1.5'
2160
+ hr['vcloud:busSubType'] = hdd_bus_sub_type
2161
+ hr['vcloud:busType'] = hdd_bus_type
2162
+ hr['vcloud:capacity'] = hdd_size
2163
+ response.css('ovf|Item').last.add_next_sibling(newhdd.doc.css('ovf|Item'))
2164
+ end
2165
+ end
2166
+
2167
+ if cfg.nics
2168
+ cfg.nics.each_with_index do |nic, i|
2169
+ next if i < nic_count
2170
+ changed = true
2171
+ nic_address_on_parent = nic_address_on_parent + 1
2172
+ instance_id = instance_id + 1
2173
+ newnic = Nokogiri::XML::Builder.new do |xml|
2174
+ xml.root('xmlns:rasd' => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData',
2175
+ 'xmlns:ovf' => 'http://schemas.dmtf.org/ovf/envelope/1') do
2176
+ xml['ovf'].Item {
2177
+ xml['rasd'].Address(nic[:mac]) if !nic[:mac].nil?
2178
+ xml['rasd'].AddressOnParent(nic_address_on_parent)
2179
+ xml['rasd'].AutomaticAllocation(true)
2180
+ xml['rasd'].Connection(nic[:network])
2181
+ xml['rasd'].Description("#{nic[:type] || :vmxnet3} ethernet adapter")
2182
+ xml['rasd'].ElementName("Network adapter #{nic_count}")
2183
+ xml['rasd'].InstanceID(instance_id)
2184
+ xml['rasd'].ResourceSubType(nic[:type] || :vmxnet3)
2185
+ xml['rasd'].ResourceType(10)
2186
+ }
2187
+ end
2188
+ end
2189
+ conn = newnic.doc.css('rasd|Connection').first
2190
+ conn['xmlns:vcloud'] = 'http://www.vmware.com/vcloud/v1.5'
2191
+ if nic[:ip_mode].upcase == 'DHCP'
2192
+ conn['vcloud:ipAddressingMode'] = 'DHCP'
2193
+ elsif nic[:ip_mode].upcase == 'STATIC'
2194
+ conn['vcloud:ipAddressingMode'] = 'MANUAL'
2195
+ conn['vcloud:ipAddress'] = nic[:ip]
2196
+ elsif nic[:ip_mode].upcase == 'POOL'
2197
+ conn['vcloud:ipAddressingMode'] = 'POOL'
2198
+ conn['vcloud:ipAddress'] = nic[:ip] if !nic[:ip].nil?
2199
+ end
2200
+ conn['vcloud:primaryNetworkConnection'] = nic[:primary]
2201
+ response.css('ovf|Item').last.add_next_sibling(newnic.doc.css('ovf|Item'))
2202
+ end
1916
2203
  end
1917
2204
 
1918
2205
  if changed
@@ -1935,6 +2222,63 @@ module VagrantPlugins
1935
2222
  end
1936
2223
 
1937
2224
 
2225
+ ##
2226
+ # Add metadata
2227
+ def set_vapp_metadata(id, data)
2228
+ task_id = set_metadata "vApp/vapp-#{id}", data
2229
+ task_id
2230
+ end
2231
+
2232
+
2233
+ ##
2234
+ # Add metadata
2235
+ def set_vm_metadata(id, data)
2236
+ task_id = set_metadata "vApp/vm-#{id}", data
2237
+ task_id
2238
+ end
2239
+
2240
+
2241
+ ##
2242
+ # Add metadata
2243
+ def set_metadata(link, data)
2244
+ params = {
2245
+ 'method' => :post,
2246
+ 'command' => "/#{link}/metadata"
2247
+ }
2248
+
2249
+ md = Nokogiri::XML::Builder.new do |xml|
2250
+ xml.Metadata('xmlns' => 'http://www.vmware.com/vcloud/v1.5',
2251
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
2252
+ 'type' => 'application/vnd.vmware.vcloud.metadata+xml') do
2253
+ data.each do |d|
2254
+ xml.MetadataEntry('type' => 'application/vnd.vmware.vcloud.metadata.value+xml') {
2255
+ xml.Key(d[0])
2256
+ if d[1].kind_of?(Integer)
2257
+ typ = 'MetadataNumberValue'
2258
+ elsif !!d[1] == d[1] # boolean
2259
+ typ = 'MetadataBooleanValue'
2260
+ else
2261
+ typ = 'MetadataStringValue'
2262
+ end
2263
+ xml.TypedValue('xsi:type' => typ) {
2264
+ xml.Value(d[1])
2265
+ }
2266
+ }
2267
+ end
2268
+ end
2269
+ end
2270
+
2271
+ _response, headers = send_request(
2272
+ params,
2273
+ md.to_xml,
2274
+ 'application/vnd.vmware.vcloud.metadata+xml'
2275
+ )
2276
+
2277
+ task_id = URI(headers['Location']).path.gsub('/api/task/', '')
2278
+ task_id
2279
+
2280
+ end
2281
+
1938
2282
  ##
1939
2283
  # Fetch details about a given VM
1940
2284
  def get_vm(vm_id)
@@ -1945,58 +2289,46 @@ module VagrantPlugins
1945
2289
 
1946
2290
  response, _headers = send_request(params)
1947
2291
 
1948
- os_desc = response.css(
1949
- 'ovf|OperatingSystemSection ovf|Description'
1950
- ).first.text
2292
+ hypervisor_enabled = response[:nestedHypervisorEnabled]
2293
+ os_desc = response.css('ovf|OperatingSystemSection ovf|Description').first.text
1951
2294
 
1952
2295
  networks = {}
2296
+ primary_network = response.css('PrimaryNetworkConnectionIndex').first.text.to_i
1953
2297
  response.css('NetworkConnection').each do |network|
1954
2298
  ip = network.css('IpAddress').first
1955
2299
  ip = ip.text if ip
2300
+ primary = false
2301
+ primary = true if network.css('NetworkConnectionIndex').first.text.to_i == primary_network
1956
2302
 
1957
2303
  networks[network['network']] = {
1958
- :index => network.css(
1959
- 'NetworkConnectionIndex'
1960
- ).first.text,
2304
+ :primary => primary,
2305
+ :index => network.css('NetworkConnectionIndex').first.text,
1961
2306
  :ip => ip,
1962
- :is_connected => network.css(
1963
- 'IsConnected'
1964
- ).first.text,
1965
- :mac_address => network.css(
1966
- 'MACAddress'
1967
- ).first.text,
1968
- :ip_allocation_mode => network.css(
1969
- 'IpAddressAllocationMode'
1970
- ).first.text
2307
+ :is_connected => network.css('IsConnected').first.text,
2308
+ :mac_address => network.css('MACAddress').first.text,
2309
+ :ip_allocation_mode => network.css('IpAddressAllocationMode').first.text
1971
2310
  }
1972
2311
  end
1973
2312
 
1974
- admin_password = response.css(
1975
- 'GuestCustomizationSection AdminPassword'
1976
- ).first
2313
+ admin_password = response.css('GuestCustomizationSection AdminPassword').first
1977
2314
  admin_password = admin_password.text if admin_password
1978
2315
 
1979
2316
  # make the lines shorter by adjusting the nokogiri css namespace
1980
2317
  guest_css = response.css('GuestCustomizationSection')
1981
2318
  guest_customizations = {
1982
2319
  :enabled => guest_css.css('Enabled').first.text,
1983
- :admin_passwd_enabled => guest_css.css(
1984
- 'AdminPasswordEnabled'
1985
- ).first.text,
1986
- :admin_passwd_auto => guest_css.css(
1987
- 'AdminPasswordAuto'
1988
- ).first.text,
2320
+ :admin_passwd_enabled => guest_css.css('AdminPasswordEnabled').first.text,
2321
+ :admin_passwd_auto => guest_css.css('AdminPasswordAuto').first.text,
1989
2322
  :admin_passwd => admin_password,
1990
- :reset_passwd_required => guest_css.css(
1991
- 'ResetPasswordRequired'
1992
- ).first.text,
2323
+ :reset_passwd_required => guest_css.css('ResetPasswordRequired').first.text,
1993
2324
  :computer_name => guest_css.css('ComputerName').first.text
1994
2325
  }
1995
2326
 
1996
2327
  {
1997
2328
  :os_desc => os_desc,
1998
2329
  :networks => networks,
1999
- :guest_customizations => guest_customizations
2330
+ :guest_customizations => guest_customizations,
2331
+ :hypervisor_enabled => hypervisor_enabled
2000
2332
  }
2001
2333
  end
2002
2334
  end # Class Version 5.1