vagrant-vcloudair 0.5.0

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/.rubocop.yml +34 -0
  4. data/Gemfile +7 -0
  5. data/LICENSE +21 -0
  6. data/README.md +109 -0
  7. data/lib/vagrant-vcloudair.rb +63 -0
  8. data/lib/vagrant-vcloudair/action.rb +298 -0
  9. data/lib/vagrant-vcloudair/action/announce_ssh_exec.rb +22 -0
  10. data/lib/vagrant-vcloudair/action/build_vapp.rb +235 -0
  11. data/lib/vagrant-vcloudair/action/connect_vcloud.rb +54 -0
  12. data/lib/vagrant-vcloudair/action/destroy_vapp.rb +54 -0
  13. data/lib/vagrant-vcloudair/action/destroy_vm.rb +37 -0
  14. data/lib/vagrant-vcloudair/action/disconnect_vcloud.rb +31 -0
  15. data/lib/vagrant-vcloudair/action/forward_ports.rb +132 -0
  16. data/lib/vagrant-vcloudair/action/handle_nat_port_collisions.rb +153 -0
  17. data/lib/vagrant-vcloudair/action/inventory_check.rb +210 -0
  18. data/lib/vagrant-vcloudair/action/is_bridged.rb +29 -0
  19. data/lib/vagrant-vcloudair/action/is_created.rb +35 -0
  20. data/lib/vagrant-vcloudair/action/is_last_vm.rb +31 -0
  21. data/lib/vagrant-vcloudair/action/is_paused.rb +20 -0
  22. data/lib/vagrant-vcloudair/action/is_running.rb +20 -0
  23. data/lib/vagrant-vcloudair/action/message_already_running.rb +16 -0
  24. data/lib/vagrant-vcloudair/action/message_cannot_suspend.rb +16 -0
  25. data/lib/vagrant-vcloudair/action/message_not_created.rb +16 -0
  26. data/lib/vagrant-vcloudair/action/message_not_running.rb +16 -0
  27. data/lib/vagrant-vcloudair/action/message_will_not_destroy.rb +21 -0
  28. data/lib/vagrant-vcloudair/action/power_off.rb +33 -0
  29. data/lib/vagrant-vcloudair/action/power_off_vapp.rb +40 -0
  30. data/lib/vagrant-vcloudair/action/power_on.rb +39 -0
  31. data/lib/vagrant-vcloudair/action/read_ssh_info.rb +153 -0
  32. data/lib/vagrant-vcloudair/action/read_state.rb +51 -0
  33. data/lib/vagrant-vcloudair/action/resume.rb +25 -0
  34. data/lib/vagrant-vcloudair/action/suspend.rb +25 -0
  35. data/lib/vagrant-vcloudair/action/unmap_port_forwardings.rb +74 -0
  36. data/lib/vagrant-vcloudair/cap/forwarded_ports.rb +38 -0
  37. data/lib/vagrant-vcloudair/cap/public_address.rb +18 -0
  38. data/lib/vagrant-vcloudair/cap/rdp_info.rb +18 -0
  39. data/lib/vagrant-vcloudair/cap/winrm_info.rb +15 -0
  40. data/lib/vagrant-vcloudair/command.rb +285 -0
  41. data/lib/vagrant-vcloudair/config.rb +205 -0
  42. data/lib/vagrant-vcloudair/driver/base.rb +643 -0
  43. data/lib/vagrant-vcloudair/driver/meta.rb +202 -0
  44. data/lib/vagrant-vcloudair/driver/version_5_1.rb +2019 -0
  45. data/lib/vagrant-vcloudair/errors.rb +77 -0
  46. data/lib/vagrant-vcloudair/model/forwarded_port.rb +66 -0
  47. data/lib/vagrant-vcloudair/plugin.rb +111 -0
  48. data/lib/vagrant-vcloudair/provider.rb +41 -0
  49. data/lib/vagrant-vcloudair/util/compile_forwarded_ports.rb +34 -0
  50. data/lib/vagrant-vcloudair/version.rb +5 -0
  51. data/locales/en.yml +169 -0
  52. data/vagrant-vcloudair.gemspec +33 -0
  53. metadata +266 -0
@@ -0,0 +1,153 @@
1
+ require 'set'
2
+
3
+ module VagrantPlugins
4
+ module VCloudAir
5
+ module Action
6
+ # This middleware class will detect and handle collisions with
7
+ # forwarded ports, whether that means raising an error or repairing
8
+ # them automatically.
9
+ #
10
+ # Parameters it takes from the environment hash:
11
+ #
12
+ # * `:port_collision_repair` - If true, it will attempt to repair
13
+ # port collisions. If false, it will raise an exception when
14
+ # there is a collision.
15
+ #
16
+ # * `:port_collision_extra_in_use` - An array of ports that are
17
+ # considered in use.
18
+ #
19
+ # * `:port_collision_remap` - A hash remapping certain host ports
20
+ # to other host ports.
21
+ #
22
+ class HandleNATPortCollisions
23
+ def initialize(app, env)
24
+ @app = app
25
+ @logger = Log4r::Logger.new(
26
+ 'vagrant_vcloudair::action::handle_port_collisions'
27
+ )
28
+ end
29
+
30
+ def call(env)
31
+ @logger.info('Detecting any forwarded port collisions...')
32
+
33
+ # Determine a list of usable ports for repair
34
+ usable_ports = Set.new(env[:machine].config.vm.usable_port_range)
35
+
36
+ # Pass one, remove all defined host ports from usable ports
37
+ with_forwarded_ports(env) do |options|
38
+ usable_ports.delete(options[:host])
39
+ end
40
+
41
+ cfg = env[:machine].provider_config
42
+ cnx = cfg.vcloudair_cnx.driver
43
+ vapp_id = env[:machine].get_vapp_id
44
+
45
+ @logger.debug('Getting VM info...')
46
+ vm_name = env[:machine].name
47
+ vm = cnx.get_vapp(vapp_id)
48
+ vm_info = vm[:vms_hash][vm_name.to_sym]
49
+
50
+ vapp_edge_ip = cnx.get_vapp_edge_public_ip(vapp_id)
51
+
52
+ @logger.debug('Getting edge gateway port forwarding rules...')
53
+ edge_gateway_rules = cnx.get_edge_gateway_rules(cfg.vdc_edge_gateway,
54
+ cfg.vdc_id)
55
+ edge_dnat_rules = edge_gateway_rules.select {|r| (r[:rule_type] == 'DNAT' && r[:translated_ip] != vapp_edge_ip)}
56
+ edge_ports_in_use = edge_dnat_rules.map{|r| r[:original_port].to_i}.to_set
57
+
58
+ @logger.debug('Getting port forwarding rules...')
59
+ vapp_nat_rules = cnx.get_vapp_port_forwarding_rules(vapp_id)
60
+ ports_in_use = vapp_nat_rules.map{|r| r[:nat_external_port].to_i}.to_set
61
+
62
+ # merge the vapp ports and the edge gateway ports together, all are in use
63
+ ports_in_use = ports_in_use | edge_ports_in_use
64
+
65
+ # Pass two, detect/handle any collisions
66
+ with_forwarded_ports(env) do |options|
67
+ guest_port = options[:guest]
68
+ host_port = options[:host]
69
+
70
+ # Find if there already is a NAT rule to guest_port of this VM
71
+ if r = vapp_nat_rules.find { |rule| (rule[:vapp_scoped_local_id] == vm_info[:vapp_scoped_local_id] &&
72
+ rule[:nat_internal_port] == guest_port.to_s) }
73
+ host_port = r[:nat_external_port].to_i
74
+ @logger.info(
75
+ "Found existing port forwarding rule #{host_port} to #{guest_port}"
76
+ )
77
+ options[:host] = host_port
78
+ options[:already_exists] = true
79
+ else
80
+ # If the port is open (listening for TCP connections)
81
+ if ports_in_use.include?(host_port)
82
+ unless options[:auto_correct]
83
+ fail Errors::ForwardPortCollision,
84
+ :guest_port => guest_port.to_s,
85
+ :host_port => host_port.to_s
86
+ end
87
+
88
+ @logger.info("Attempting to repair FP collision: #{host_port}")
89
+
90
+ repaired_port = nil
91
+ until usable_ports.empty?
92
+ # Attempt to repair the forwarded port
93
+ repaired_port = usable_ports.to_a.sort[0]
94
+ usable_ports.delete(repaired_port)
95
+
96
+ # If the port is in use, then we can't use this either...
97
+ if ports_in_use.include?(repaired_port)
98
+ @logger.info(
99
+ "Repaired port also in use: #{repaired_port}." +
100
+ 'Trying another...'
101
+ )
102
+ next
103
+ end
104
+
105
+ # We have a port so break out
106
+ break
107
+ end
108
+
109
+ # If we have no usable ports then we can't repair
110
+ if !repaired_port && usable_ports.empty?
111
+ fail Errors::ForwardPortAutolistEmpty,
112
+ :vm_name => env[:machine].name,
113
+ :guest_port => guest_port.to_s,
114
+ :host_port => host_port.to_s
115
+ end
116
+
117
+ # Modify the args in place
118
+ options[:host] = repaired_port
119
+
120
+ @logger.info(
121
+ "Repaired FP collision: #{host_port} to #{repaired_port}"
122
+ )
123
+
124
+ # Notify the user
125
+ env[:ui].info(
126
+ I18n.t(
127
+ 'vagrant.actions.vm.forward_ports.fixed_collision',
128
+ :host_port => host_port.to_s,
129
+ :guest_port => guest_port.to_s,
130
+ :new_port => repaired_port.to_s
131
+ )
132
+ )
133
+ end
134
+ end
135
+ end
136
+
137
+ @app.call(env)
138
+ end
139
+
140
+ protected
141
+
142
+ def with_forwarded_ports(env)
143
+ env[:machine].config.vm.networks.each do |type, options|
144
+ # Ignore anything but forwarded ports
145
+ next if type != :forwarded_port
146
+
147
+ yield options
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,210 @@
1
+ require 'etc'
2
+
3
+ module VagrantPlugins
4
+ module VCloudAir
5
+ module Action
6
+ class InventoryCheck
7
+ def initialize(app, env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new('vagrant_vcloudair::action::inventory_check')
10
+ end
11
+
12
+ def call(env)
13
+ vcloud_check_inventory(env)
14
+
15
+ @app.call env
16
+ end
17
+
18
+ def vcloud_upload_box(env)
19
+ cfg = env[:machine].provider_config
20
+ cnx = cfg.vcloudair_cnx.driver
21
+
22
+ box_dir = env[:machine].box.directory.to_s
23
+
24
+ if env[:machine].box.name.to_s.include? '/'
25
+ box_file = env[:machine].box.name.rpartition('/').last.to_s
26
+ box_name = env[:machine].box.name.to_s
27
+ else
28
+ box_file = env[:machine].box.name.to_s
29
+ box_name = box_file
30
+ end
31
+
32
+ box_ovf = "#{box_dir}/#{box_file}.ovf"
33
+
34
+ # Still relying on ruby-progressbar because report_progress
35
+ # basically sucks.
36
+ @logger.debug("OVF File: #{box_ovf}")
37
+ upload_ovf = cnx.upload_ovf(
38
+ cfg.vdc_id,
39
+ box_name,
40
+ 'Vagrant Box',
41
+ box_ovf,
42
+ cfg.catalog_id,
43
+ {
44
+ :progressbar_enable => true,
45
+ # Set the chunksize for upload at the configured value or default
46
+ # to 5M
47
+ :chunksize => (cfg.upload_chunksize || 5_242_880)
48
+ }
49
+ )
50
+
51
+ env[:ui].info(I18n.t('vagrant_vcloudair.catalog.add_to_catalog',
52
+ box_name: box_name,
53
+ catalog_name: cfg.catalog_name))
54
+
55
+ add_ovf_to_catalog = cnx.wait_task_completion(upload_ovf)
56
+
57
+ unless add_ovf_to_catalog[:errormsg].nil?
58
+ fail Errors::CatalogAddError,
59
+ :message => add_ovf_to_catalog[:errormsg]
60
+ end
61
+
62
+ # Retrieve catalog_item ID
63
+ cfg.catalog_item = cnx.get_catalog_item_by_name(
64
+ cfg.catalog_id,
65
+ box_name
66
+ )
67
+ end
68
+
69
+ def vcloud_create_catalog(env)
70
+ cfg = env[:machine].provider_config
71
+ cnx = cfg.vcloudair_cnx.driver
72
+
73
+ catalog_creation = cnx.create_catalog(
74
+ cfg.org_id,
75
+ cfg.catalog_name,
76
+ "Created by #{Etc.getlogin} " +
77
+ "running on #{Socket.gethostname.downcase} " +
78
+ "using vagrant-vcloudair on #{Time.now.strftime("%B %d, %Y")}"
79
+ )
80
+ cnx.wait_task_completion(catalog_creation[:task_id])
81
+
82
+ @logger.debug("Catalog Creation result: #{catalog_creation.inspect}")
83
+ env[:ui].info(I18n.t('vagrant_vcloudair.catalog.create_catalog',
84
+ catalog_name: cfg.catalog_name))
85
+
86
+ cfg.catalog_id = catalog_creation[:catalog_id]
87
+ end
88
+
89
+ def vcloud_check_inventory(env)
90
+ # Will check each mandatory config value against the vCloud Air
91
+ # Instance and will setup the global environment config values
92
+ cfg = env[:machine].provider_config
93
+ cnx = cfg.vcloudair_cnx.driver
94
+
95
+ if env[:machine].box.name.to_s.include? '/'
96
+ box_file = env[:machine].box.name.rpartition('/').last.to_s
97
+ box_name = env[:machine].box.name.to_s
98
+ else
99
+ box_file = env[:machine].box.name.to_s
100
+ box_name = box_file
101
+ end
102
+
103
+ cfg.org = cnx.get_organization_by_name(cfg.vdc_name)
104
+ @logger.debug("cfg.org: #{cfg.org}")
105
+ cfg.org_id = cnx.get_organization_id_by_name(cfg.vdc_name)
106
+ @logger.debug("cfg.org_id: #{cfg.org_id}")
107
+
108
+ cfg.vdc = cnx.get_vdc_by_name(cfg.org, cfg.vdc_name)
109
+ cfg.vdc_id = cnx.get_vdc_id_by_name(cfg.org, cfg.vdc_name)
110
+
111
+ cfg.catalog = cnx.get_catalog_by_name(cfg.org, cfg.catalog_name)
112
+ cfg.catalog_id = cnx.get_catalog_id_by_name(cfg.org, cfg.catalog_name)
113
+
114
+ if cfg.catalog_id.nil?
115
+ env[:ui].warn(I18n.t(
116
+ 'vagrant_vcloudair.catalog.nonexistant_catalog',
117
+ catalog_name: cfg.catalog_name))
118
+
119
+ user_input = env[:ui].ask(
120
+ I18n.t('vagrant_vcloudair.catalog.create_catalog_ask',
121
+ catalog_name: cfg.catalog_name) + ' '
122
+ )
123
+
124
+ if user_input.downcase == 'yes' || user_input.downcase == 'y'
125
+ vcloud_create_catalog(env)
126
+ else
127
+ env[:ui].error(I18n.t(
128
+ 'vagrant_vcloudair.catalog.catalog_not_created'))
129
+ fail Errors::WontCreate, :item => 'Catalog'
130
+ end
131
+ end
132
+
133
+ @logger.debug(
134
+ "Getting catalog item with cfg.catalog_id: [#{cfg.catalog_id}] " +
135
+ "and machine name [#{box_name}]"
136
+ )
137
+ cfg.catalog_item = cnx.get_catalog_item_by_name(
138
+ cfg.catalog_id,
139
+ box_name
140
+ )
141
+
142
+ @logger.debug("Catalog item is now #{cfg.catalog_item}")
143
+
144
+ # This only works with Org Admin role or higher
145
+
146
+ cfg.vdc_network_id = cfg.org[:networks][cfg.vdc_network_name]
147
+ unless cfg.vdc_network_id
148
+ # TODO: TEMP FIX: permissions issues at the Org Level for vApp
149
+ # authors to "view" Org vDC Networks but they can see them at the
150
+ # Organization vDC level (tsugliani)
151
+ cfg.vdc_network_id = cfg.vdc[:networks][cfg.vdc_network_name]
152
+ fail Errors::InvalidNetSpecification unless cfg.vdc_network_id
153
+ end
154
+
155
+ # Checking Catalog mandatory requirements
156
+ if !cfg.catalog_id
157
+ @logger.info("Catalog [#{cfg.catalog_name}] STILL does not exist!")
158
+ fail Errors::ObjectNotFound,
159
+ :message => 'Catalog not found after creation'
160
+ else
161
+ @logger.info("Catalog [#{cfg.catalog_name}] exists")
162
+ end
163
+
164
+ if !cfg.catalog_item
165
+ env[:ui].warn(I18n.t(
166
+ 'vagrant_vcloudair.catalog.nonexistant_catalog_item',
167
+ catalog_item: box_name,
168
+ catalog_name: cfg.catalog_name))
169
+
170
+ user_input = env[:ui].ask(
171
+ I18n.t('vagrant_vcloudair.catalog.upload_ask',
172
+ catalog_item: box_name,
173
+ catalog_name: cfg.catalog_name) + ' ')
174
+
175
+ if user_input.downcase == 'yes' || user_input.downcase == 'y'
176
+ env[:ui].info(I18n.t('vagrant_vcloudair.catalog.uploading',
177
+ catalog_item: box_name))
178
+ vcloud_upload_box(env)
179
+ else
180
+ env[:ui].error(
181
+ I18n.t('vagrant_vcloudair.catalog.catalog_item_notavailable'))
182
+ fail Errors::WontCreate, :item => 'Box'
183
+ end
184
+
185
+ else
186
+ @logger.info(
187
+ "Using catalog item [#{box_name}] " +
188
+ "in Catalog [#{cfg.catalog_name}]..."
189
+ )
190
+ end
191
+
192
+ # Test if Gateway Edge and its IP are correct
193
+ if !cfg.vdc_edge_gateway.nil? && !cfg.vdc_edge_gateway_ip.nil?
194
+ env[:ui].info(I18n.t('vagrant_vcloudair.edge.network_test'))
195
+ # Test if Edge Gateway exists
196
+ cnx.find_edge_gateway_id(cfg.vdc_edge_gateway, cfg.vdc_id)
197
+ # Test if Edge Gateway IP exists
198
+ cnx.find_edge_gateway_network(cfg.vdc_edge_gateway,
199
+ cfg.vdc_id,
200
+ cfg.vdc_edge_gateway_ip)
201
+ # Test if Network is connected to Edge Gateway
202
+ cnx.check_edge_gateway_network(cfg.vdc_edge_gateway,
203
+ cfg.vdc_id,
204
+ cfg.vdc_network_name)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,29 @@
1
+ module VagrantPlugins
2
+ module VCloudAir
3
+ module Action
4
+ class IsBridged
5
+ def initialize(app, env)
6
+ @app = app
7
+ @logger = Log4r::Logger.new('vagrant_vcloudair::action::is_bridged')
8
+ end
9
+
10
+ def call(env)
11
+ vapp_id = env[:machine].get_vapp_id
12
+
13
+ cfg = env[:machine].provider_config
14
+ cnx = cfg.vcloudair_cnx.driver
15
+
16
+ begin
17
+ @logger.debug('Trying to get the vApp port forwarding rules')
18
+ cnx.get_vapp_port_forwarding_rules(vapp_id)
19
+ rescue
20
+ @logger.debug('Setting the bridged_network environment var to true')
21
+ env[:bridged_network] = true
22
+ end
23
+
24
+ @app.call env
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module VagrantPlugins
2
+ module VCloudAir
3
+ module Action
4
+ class IsCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ @logger = Log4r::Logger.new('vagrant_vcloudair::action::is_created')
8
+ end
9
+
10
+ def call(env)
11
+ vapp_id = env[:machine].get_vapp_id
12
+
13
+ if vapp_id.nil?
14
+ @logger.warn('vApp has not been created')
15
+ env[:result] = false
16
+ else
17
+ @logger.info("vApp has been created and ID is: [#{vapp_id}]")
18
+
19
+ vm_id = env[:machine].id
20
+ if vm_id
21
+ @logger.info("VM has been added to vApp and ID is: [#{vm_id}]")
22
+ env[:result] = true
23
+ else
24
+ @logger.warn('VM has not been added to vApp')
25
+ env[:result] = false
26
+ end
27
+
28
+ end
29
+
30
+ @app.call env
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module VCloudAir
3
+ module Action
4
+ class IsLastVM
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ cfg = env[:machine].provider_config
11
+ cnx = cfg.vcloudair_cnx.driver
12
+
13
+ vapp_id = env[:machine].get_vapp_id
14
+
15
+ test_vapp = cnx.get_vapp(vapp_id)
16
+
17
+ if test_vapp[:vms_hash].count == 1
18
+ # Set the result to be true if the machine is running.
19
+ env[:result] = true
20
+ else
21
+ env[:result] = false
22
+ end
23
+
24
+ # Call the next if we have one (but we shouldn't, since this
25
+ # middleware is built to run with the Call-type middlewares)
26
+ @app.call(env)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end