vcloud-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +9 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +103 -0
  5. data/Rakefile +18 -0
  6. data/bin/vcloud-query +84 -0
  7. data/jenkins.sh +10 -0
  8. data/lib/vcloud/core.rb +23 -0
  9. data/lib/vcloud/core/compute_metadata.rb +13 -0
  10. data/lib/vcloud/core/edge_gateway.rb +46 -0
  11. data/lib/vcloud/core/entity.rb +23 -0
  12. data/lib/vcloud/core/metadata_helper.rb +29 -0
  13. data/lib/vcloud/core/org_vdc_network.rb +102 -0
  14. data/lib/vcloud/core/query.rb +142 -0
  15. data/lib/vcloud/core/vapp.rb +118 -0
  16. data/lib/vcloud/core/vapp_template.rb +39 -0
  17. data/lib/vcloud/core/vdc.rb +37 -0
  18. data/lib/vcloud/core/version.rb +5 -0
  19. data/lib/vcloud/core/vm.rb +162 -0
  20. data/lib/vcloud/fog.rb +5 -0
  21. data/lib/vcloud/fog/content_types.rb +20 -0
  22. data/lib/vcloud/fog/model_interface.rb +33 -0
  23. data/lib/vcloud/fog/relation.rb +8 -0
  24. data/lib/vcloud/fog/service_interface.rb +257 -0
  25. data/spec/spec_helper.rb +26 -0
  26. data/spec/support/stub_fog_interface.rb +59 -0
  27. data/spec/vcloud/core/edge_gateway_spec.rb +79 -0
  28. data/spec/vcloud/core/metadata_helper_spec.rb +89 -0
  29. data/spec/vcloud/core/org_vdc_network_spec.rb +257 -0
  30. data/spec/vcloud/core/query_spec.rb +111 -0
  31. data/spec/vcloud/core/vapp_spec.rb +173 -0
  32. data/spec/vcloud/core/vapp_template_spec.rb +77 -0
  33. data/spec/vcloud/core/vdc_spec.rb +68 -0
  34. data/spec/vcloud/core/vm_spec.rb +290 -0
  35. data/spec/vcloud/data/basic_preamble_test.erb +8 -0
  36. data/spec/vcloud/data/basic_preamble_test.erb.OUT +8 -0
  37. data/spec/vcloud/fog/fog_model_interface_spec.rb +25 -0
  38. data/spec/vcloud/fog/service_interface_spec.rb +30 -0
  39. data/vcloud-core.gemspec +30 -0
  40. metadata +198 -0
@@ -0,0 +1,142 @@
1
+ require 'csv'
2
+
3
+ module Vcloud
4
+ class Query
5
+
6
+ attr_reader :type
7
+ attr_reader :options
8
+
9
+ def initialize(type=nil, options={})
10
+ @type = type
11
+ @options = options
12
+ @options[:output_format] ||= 'tsv'
13
+ Fog.mock! if ENV['FOG_MOCK'] || options[:mock]
14
+ @fsi = Vcloud::Fog::ServiceInterface.new
15
+ end
16
+
17
+ def filter
18
+ options[:filter]
19
+ end
20
+
21
+ def output_format
22
+ options[:output_format]
23
+ end
24
+
25
+ def fields
26
+ options[:fields]
27
+ end
28
+
29
+ def run()
30
+
31
+ puts "options:" if @options[:debug]
32
+ pp @options if @options[:debug]
33
+
34
+ if @type.nil?
35
+ output_potential_query_types
36
+ else
37
+ output_query_results
38
+ end
39
+ end
40
+
41
+ def get_all_results
42
+ results = []
43
+ (1..get_num_pages).each do |page|
44
+ results += get_results_page(page) || []
45
+ end
46
+ results
47
+ end
48
+
49
+ private
50
+
51
+ def get_num_pages
52
+ body = @fsi.get_execute_query(type=@type, @options)
53
+ last_page = body[:lastPage] || 1
54
+ raise "Invalid lastPage (#{last_page}) in query results" unless last_page.is_a? Integer
55
+ return last_page.to_i
56
+ end
57
+
58
+ def get_results_page(page)
59
+ raise "Must supply a page number" if page.nil?
60
+
61
+ begin
62
+ body = @fsi.get_execute_query(type=@type, @options.merge({:page=>page}))
63
+ pp body if @options[:debug]
64
+ rescue ::Fog::Compute::VcloudDirector::BadRequest, ::Fog::Compute::VcloudDirector::Forbidden => e
65
+ raise "Access denied: #{e.message}"
66
+ end
67
+
68
+ records = body.keys.detect {|key| key.to_s =~ /Record|Reference$/}
69
+ body[records] = [body[records]] if body[records].is_a?(Hash)
70
+ return nil if body[records].nil? || body[records].empty?
71
+ body[records]
72
+
73
+ end
74
+
75
+ def output_query_results
76
+ results = get_all_results
77
+ output_header(results)
78
+ output_results(results)
79
+ end
80
+
81
+ def output_potential_query_types
82
+
83
+ query_list = @fsi.get_execute_query
84
+ queries = {}
85
+ type_width = 0
86
+ query_list[:Link].select do |link|
87
+ link[:rel] == 'down'
88
+ end.map do |link|
89
+ href = Nokogiri::XML.fragment(link[:href])
90
+ query = CGI.parse(URI.parse(href.text).query)
91
+ [query['type'].first, query['format'].first]
92
+ end.each do |type, format|
93
+ queries[type] ||= []
94
+ queries[type] << format
95
+ type_width = [type_width, type.size].max
96
+ end
97
+ queries.keys.sort.each do |type|
98
+ puts "%-#{type_width}s %s" % [type, queries[type].sort.join(',')]
99
+ end
100
+
101
+ end
102
+
103
+ def output_header(results)
104
+ return if results.size == 0
105
+ case @options[:output_format]
106
+ when 'csv'
107
+ csv_string = CSV.generate do |csv|
108
+ csv << results.first.keys
109
+ end
110
+ puts csv_string
111
+ when 'tsv'
112
+ puts results.first.keys.join("\t")
113
+ end
114
+ end
115
+
116
+ def output_results(results)
117
+ return if results.size == 0
118
+
119
+ case @options[:output_format]
120
+ when 'yaml'
121
+ puts YAML.dump(results)
122
+ when 'csv'
123
+ csv_string = CSV.generate do |csv|
124
+ results.each do |record|
125
+ csv << record.values
126
+ end
127
+ end
128
+ puts csv_string
129
+ when 'tsv'
130
+ puts results.first.keys.join("\t") if @options[:page] == 1
131
+ results.each do |record|
132
+ puts record.values.join("\t")
133
+ end
134
+ else
135
+ raise "Unsupported output format #{@options[:output_format]}"
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+ end
142
+
@@ -0,0 +1,118 @@
1
+ module Vcloud
2
+ module Core
3
+ class Vapp
4
+ extend ComputeMetadata
5
+
6
+ attr_reader :id
7
+
8
+ def initialize(id)
9
+ unless id =~ /^#{self.class.id_prefix}-[-0-9a-f]+$/
10
+ raise "#{self.class.id_prefix} id : #{id} is not in correct format"
11
+ end
12
+ @id = id
13
+ end
14
+
15
+ def self.get_by_name(name)
16
+ q = Query.new('vApp', :filter => "name==#{name}")
17
+ unless res = q.get_all_results
18
+ raise "Error finding vApp by name #{name}"
19
+ end
20
+ case res.size
21
+ when 0
22
+ raise "vApp #{name} not found"
23
+ when 1
24
+ return self.new(res.first[:href].split('/').last)
25
+ else
26
+ raise "found multiple vApp entities with name #{name}!"
27
+ end
28
+ end
29
+
30
+ def vcloud_attributes
31
+ Vcloud::Fog::ServiceInterface.new.get_vapp(id)
32
+ end
33
+
34
+ module STATUS
35
+ RUNNING = 4
36
+ end
37
+
38
+ def name
39
+ vcloud_attributes[:name]
40
+ end
41
+
42
+ def href
43
+ vcloud_attributes[:href]
44
+ end
45
+
46
+ def vdc_id
47
+ link = vcloud_attributes[:Link].detect { |l| l[:rel] == Fog::RELATION::PARENT && l[:type] == Fog::ContentTypes::VDC }
48
+ link ? link[:href].split('/').last : raise('a vapp without parent vdc found')
49
+ end
50
+
51
+ def fog_vms
52
+ vcloud_attributes[:Children][:Vm]
53
+ end
54
+
55
+ def networks
56
+ vcloud_attributes[:'ovf:NetworkSection'][:'ovf:Network']
57
+ end
58
+
59
+ def self.get_by_name_and_vdc_name(name, vdc_name)
60
+ fog_interface = Vcloud::Fog::ServiceInterface.new
61
+ attrs = fog_interface.get_vapp_by_name_and_vdc_name(name, vdc_name)
62
+ self.new(attrs[:href].split('/').last) if attrs && attrs.key?(:href)
63
+ end
64
+
65
+ def self.instantiate(name, network_names, template_id, vdc_name)
66
+ Vcloud::Core.logger.info("Instantiating new vApp #{name} in vDC '#{vdc_name}'")
67
+ fog_interface = Vcloud::Fog::ServiceInterface.new
68
+ networks = get_networks(network_names, vdc_name)
69
+
70
+ attrs = fog_interface.post_instantiate_vapp_template(
71
+ fog_interface.vdc(vdc_name),
72
+ template_id,
73
+ name,
74
+ InstantiationParams: build_network_config(networks)
75
+ )
76
+ self.new(attrs[:href].split('/').last) if attrs and attrs.key?(:href)
77
+ end
78
+
79
+ def power_on
80
+ raise "Cannot power on a missing vApp." unless id
81
+ return true if running?
82
+ Vcloud::Fog::ServiceInterface.new.power_on_vapp(id)
83
+ running?
84
+ end
85
+
86
+ private
87
+ def running?
88
+ raise "Cannot call running? on a missing vApp." unless id
89
+ vapp = Vcloud::Fog::ServiceInterface.new.get_vapp(id)
90
+ vapp[:status].to_i == STATUS::RUNNING ? true : false
91
+ end
92
+
93
+ def self.build_network_config(networks)
94
+ return {} unless networks
95
+ instantiation = { NetworkConfigSection: {NetworkConfig: []} }
96
+ networks.compact.each do |network|
97
+ instantiation[:NetworkConfigSection][:NetworkConfig] << {
98
+ networkName: network[:name],
99
+ Configuration: {
100
+ ParentNetwork: {href: network[:href]},
101
+ FenceMode: 'bridged',
102
+ }
103
+ }
104
+ end
105
+ instantiation
106
+ end
107
+
108
+ def self.id_prefix
109
+ 'vapp'
110
+ end
111
+
112
+ def self.get_networks(network_names, vdc_name)
113
+ fsi = Vcloud::Fog::ServiceInterface.new
114
+ fsi.find_networks(network_names, vdc_name) if network_names
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,39 @@
1
+ module Vcloud
2
+ module Core
3
+ class VappTemplate
4
+
5
+ attr_reader :id
6
+
7
+ def initialize(id)
8
+ unless id =~ /^#{self.class.id_prefix}-[-0-9a-f]+$/
9
+ raise "#{self.class.id_prefix} id : #{id} is not in correct format"
10
+ end
11
+ @id = id
12
+ end
13
+
14
+ def vcloud_attributes
15
+ Vcloud::Fog::ServiceInterface.new.get_vapp_template(id)
16
+ end
17
+
18
+ def href
19
+ vcloud_attributes[:href]
20
+ end
21
+
22
+ def name
23
+ vcloud_attributes[:name]
24
+ end
25
+
26
+ def self.get catalog_name, catalog_item_name
27
+ raise "provide catalog and catalog item name to load vappTemplate" unless catalog_name && catalog_item_name
28
+ body = Vcloud::Fog::ServiceInterface.new.template(catalog_name, catalog_item_name)
29
+ raise 'Could not find template vApp' unless body && body.key?(:href)
30
+ self.new(body[:href].split('/').last)
31
+ end
32
+
33
+ def self.id_prefix
34
+ 'vappTemplate'
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ module Vcloud
2
+ module Core
3
+ class Vdc
4
+
5
+ attr_reader :id
6
+
7
+ def initialize(id)
8
+ unless id =~ /^[-0-9a-f]+$/
9
+ raise "vdc id : #{id} is not in correct format"
10
+ end
11
+ @id = id
12
+ end
13
+
14
+ def self.get_by_name(name)
15
+ q = Query.new('orgVdc', :filter => "name==#{name}")
16
+ unless res = q.get_all_results
17
+ raise "Error finding vDC by name #{name}"
18
+ end
19
+ raise "vDc #{name} not found" unless res.size == 1
20
+ return self.new(res.first[:href].split('/').last)
21
+ end
22
+
23
+ def vcloud_attributes
24
+ Vcloud::Fog::ServiceInterface.new.get_vdc(id)
25
+ end
26
+
27
+ def name
28
+ vcloud_attributes[:name]
29
+ end
30
+
31
+ def href
32
+ vcloud_attributes[:href]
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module Vcloud
2
+ module Core
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,162 @@
1
+ module Vcloud
2
+ module Core
3
+ class Vm
4
+ extend ComputeMetadata
5
+
6
+ attr_reader :id
7
+
8
+ def initialize(id, vapp)
9
+ unless id =~ /^#{self.class.id_prefix}-[-0-9a-f]+$/
10
+ raise "#{self.class.id_prefix} id : #{id} is not in correct format"
11
+ end
12
+ @id = id
13
+ @vapp = vapp
14
+ end
15
+
16
+ def vcloud_attributes
17
+ Vcloud::Fog::ServiceInterface.new.get_vapp(id)
18
+ end
19
+
20
+ def update_memory_size_in_mb(new_memory)
21
+ return if new_memory.nil?
22
+ return if new_memory.to_i < 64
23
+ unless memory.to_i == new_memory.to_i
24
+ Vcloud::Fog::ServiceInterface.new.put_memory(id, new_memory)
25
+ end
26
+ end
27
+
28
+ def memory
29
+ memory_item = virtual_hardware_section.detect { |i| i[:'rasd:ResourceType'] == '4' }
30
+ memory_item[:'rasd:VirtualQuantity']
31
+ end
32
+
33
+ def cpu
34
+ cpu_item = virtual_hardware_section.detect { |i| i[:'rasd:ResourceType'] == '3' }
35
+ cpu_item[:'rasd:VirtualQuantity']
36
+ end
37
+
38
+ def name
39
+ vcloud_attributes[:name]
40
+ end
41
+
42
+ def href
43
+ vcloud_attributes[:href]
44
+ end
45
+
46
+ def update_name(new_name)
47
+ fsi = Vcloud::Fog::ServiceInterface.new
48
+ fsi.put_vm(id, new_name) unless name == new_name
49
+ end
50
+
51
+ def vapp_name
52
+ @vapp.name
53
+ end
54
+
55
+ def update_cpu_count(new_cpu_count)
56
+ return if new_cpu_count.nil?
57
+ return if new_cpu_count.to_i == 0
58
+ unless cpu.to_i == new_cpu_count.to_i
59
+ Vcloud::Fog::ServiceInterface.new.put_cpu(id, new_cpu_count)
60
+ end
61
+ end
62
+
63
+ def update_metadata(metadata)
64
+ return if metadata.nil?
65
+ fsi = Vcloud::Fog::ServiceInterface.new
66
+ metadata.each do |k, v|
67
+ fsi.put_vapp_metadata_value(@vapp.id, k, v)
68
+ fsi.put_vapp_metadata_value(id, k, v)
69
+ end
70
+ end
71
+
72
+ def add_extra_disks(extra_disks)
73
+ vm = Vcloud::Fog::ModelInterface.new.get_vm_by_href(href)
74
+ if extra_disks
75
+ extra_disks.each do |extra_disk|
76
+ Vcloud::Core.logger.info("adding a disk of size #{extra_disk[:size]}MB into VM #{id}")
77
+ vm.disks.create(extra_disk[:size])
78
+ end
79
+ end
80
+ end
81
+
82
+ def configure_network_interfaces(networks_config)
83
+ return unless networks_config
84
+ section = {PrimaryNetworkConnectionIndex: 0}
85
+ section[:NetworkConnection] = networks_config.compact.each_with_index.map do |network, i|
86
+ connection = {
87
+ network: network[:name],
88
+ needsCustomization: true,
89
+ NetworkConnectionIndex: i,
90
+ IsConnected: true
91
+ }
92
+ ip_address = network[:ip_address]
93
+ connection[:IpAddress] = ip_address unless ip_address.nil?
94
+ connection[:IpAddressAllocationMode] = ip_address ? 'MANUAL' : 'DHCP'
95
+ connection
96
+ end
97
+ Vcloud::Fog::ServiceInterface.new.put_network_connection_system_section_vapp(id, section)
98
+ end
99
+
100
+ def configure_guest_customization_section(name, bootstrap_config, extra_disks)
101
+ if bootstrap_config.nil? or bootstrap_config[:script_path].nil?
102
+ interpolated_preamble = ''
103
+ else
104
+ preamble_vars = bootstrap_config[:vars].merge(:extra_disks => extra_disks)
105
+ interpolated_preamble = generate_preamble(
106
+ bootstrap_config[:script_path],
107
+ bootstrap_config[:script_post_processor],
108
+ preamble_vars,
109
+ )
110
+ end
111
+ Vcloud::Fog::ServiceInterface.new.put_guest_customization_section(id, name, interpolated_preamble)
112
+ end
113
+
114
+ def generate_preamble(script_path, script_post_processor, vars)
115
+ vapp_name = @vapp.name
116
+ script = ERB.new(File.read(File.expand_path(script_path)), nil, '>-')
117
+ .result(binding)
118
+ if script_post_processor
119
+ script = Open3.capture2(File.expand_path(script_post_processor),
120
+ stdin_data: script).first
121
+ end
122
+ script
123
+ end
124
+
125
+ def update_storage_profile storage_profile
126
+ storage_profile_href = get_storage_profile_href_by_name(storage_profile, @vapp.name)
127
+ Vcloud::Fog::ServiceInterface.new.put_vm(id, name, {
128
+ :StorageProfile => {
129
+ name: storage_profile,
130
+ href: storage_profile_href
131
+ }
132
+ })
133
+ end
134
+
135
+ private
136
+ def virtual_hardware_section
137
+ vcloud_attributes[:'ovf:VirtualHardwareSection'][:'ovf:Item']
138
+ end
139
+
140
+ def get_storage_profile_href_by_name(storage_profile_name, vapp_name)
141
+ q = Query.new('vApp', :filter => "name==#{vapp_name}")
142
+ vdc_results = q.get_all_results
143
+ vdc_name = vdc_results.first[:vdcName]
144
+
145
+ q = Query.new('orgVdcStorageProfile', :filter => "name==#{storage_profile_name};vdcName==#{vdc_name}")
146
+ sp_results = q.get_all_results
147
+
148
+ if sp_results.empty? or !sp_results.first.has_key?(:href)
149
+ raise "storage profile not found"
150
+ else
151
+ return sp_results.first[:href]
152
+ end
153
+ end
154
+
155
+ def self.id_prefix
156
+ 'vm'
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+ end