vagrant-vcloud 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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