chef-provisioning-oneview 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +119 -65
  3. data/chef-provisioning-oneview.gemspec +39 -0
  4. data/lib/chef/provisioning/create_machine.rb +43 -46
  5. data/lib/chef/provisioning/customize_machine.rb +50 -48
  6. data/lib/chef/provisioning/helpers.rb +18 -0
  7. data/lib/chef/provisioning/icsp/api_v104.rb +3 -2
  8. data/lib/chef/provisioning/icsp/icsp_helper.rb +288 -0
  9. data/lib/chef/provisioning/icsp/rest.rb +50 -0
  10. data/lib/chef/provisioning/oneview/oneview_helper.rb +53 -0
  11. data/lib/chef/provisioning/oneview/san_storage.rb +69 -69
  12. data/lib/chef/provisioning/oneview_driver.rb +124 -105
  13. data/lib/chef/provisioning/version.rb +1 -1
  14. metadata +23 -61
  15. data/Rakefile +0 -19
  16. data/lib/chef/provisioning/icsp/icsp_api.rb +0 -284
  17. data/lib/chef/provisioning/oneview/oneview_api.rb +0 -186
  18. data/lib/chef/provisioning/oneview/v1.2/api.rb +0 -3
  19. data/lib/chef/provisioning/oneview/v2.0/api.rb +0 -3
  20. data/lib/chef/provisioning/rest.rb +0 -52
  21. data/spec/shared_context.rb +0 -78
  22. data/spec/spec_helper.rb +0 -23
  23. data/spec/support/fake_action_handler.rb +0 -6
  24. data/spec/support/fake_icsp.rb +0 -73
  25. data/spec/support/fake_machine_spec.rb +0 -18
  26. data/spec/support/fake_oneview.rb +0 -148
  27. data/spec/support/fixtures/icsp/v102/error_404.json +0 -14
  28. data/spec/support/fixtures/icsp/v102/login.json +0 -5
  29. data/spec/support/fixtures/icsp/v102/os-deployment-build-plans.json +0 -99
  30. data/spec/support/fixtures/icsp/v102/os-deployment-servers.json +0 -179
  31. data/spec/support/fixtures/icsp/v102/os-deployment-servers_1670001.json +0 -83
  32. data/spec/support/fixtures/icsp/v102/os-deployment-servers_fakesn.json +0 -9
  33. data/spec/support/fixtures/icsp/v102/server_by_sn_VCGE9KB041.json +0 -45
  34. data/spec/support/fixtures/icsp/v102/server_by_sn_empty.json +0 -16
  35. data/spec/support/fixtures/icsp/v102/version.json +0 -4
  36. data/spec/support/fixtures/oneview/v120/error_404.json +0 -14
  37. data/spec/support/fixtures/oneview/v120/login.json +0 -5
  38. data/spec/support/fixtures/oneview/v120/server-hardware.json +0 -1476
  39. data/spec/support/fixtures/oneview/v120/server-hardware_Template-WebServer.json +0 -468
  40. data/spec/support/fixtures/oneview/v120/server-hardware_specific.json +0 -151
  41. data/spec/support/fixtures/oneview/v120/server-profiles.json +0 -369
  42. data/spec/support/fixtures/oneview/v120/server-profiles_invalid_filter.json +0 -14
  43. data/spec/support/fixtures/oneview/v120/server-profiles_name_Template-WebServer.json +0 -133
  44. data/spec/support/fixtures/oneview/v120/server-profiles_name_Template-WebServerWithSAN.json +0 -200
  45. data/spec/support/fixtures/oneview/v120/server-profiles_name_chef-web01.json +0 -133
  46. data/spec/support/fixtures/oneview/v120/server-profiles_name_chef-web03.json +0 -133
  47. data/spec/support/fixtures/oneview/v120/server-profiles_name_empty.json +0 -15
  48. data/spec/support/fixtures/oneview/v120/server-profiles_sn_VCGE9KB041.json +0 -133
  49. data/spec/support/fixtures/oneview/v120/server-profiles_sn_VCGE9KB042.json +0 -206
  50. data/spec/support/fixtures/oneview/v120/server-profiles_sn_empty.json +0 -15
  51. data/spec/support/fixtures/oneview/v120/storage-volumes_1B5D3CA2-6C5B-41C2-8B97-1821F1883F22.json +0 -26
  52. data/spec/support/fixtures/oneview/v120/tasks_fake_active.json +0 -5
  53. data/spec/support/fixtures/oneview/v120/tasks_fake_complete.json +0 -5
  54. data/spec/support/fixtures/oneview/v120/version.json +0 -4
  55. data/spec/support/fixtures/oneview/v200/server-profile-templates_WebServerTemplate.json +0 -109
  56. data/spec/support/fixtures/oneview/v200/server-profile-templates_WebServerTemplateWithSAN.json +0 -144
  57. data/spec/support/fixtures/oneview/v200/server-profile-templates_invalid.json +0 -16
  58. data/spec/support/fixtures/oneview/v200/server-profile-templates_new-profile_WebServerTemplate.json +0 -125
  59. data/spec/support/fixtures/oneview/v200/server-profile-templates_new-profile_WebServerTemplateWithSAN.json +0 -178
  60. data/spec/support/fixtures/oneview/v200/version.json +0 -4
  61. data/spec/unit/create_machine_spec.rb +0 -78
  62. data/spec/unit/destroy_spec.rb +0 -26
  63. data/spec/unit/icsp_nic_teams_spec.rb +0 -38
  64. data/spec/unit/icsp_search_spec.rb +0 -25
  65. data/spec/unit/oneview_driver_spec.rb +0 -79
  66. data/spec/unit/oneview_login_spec.rb +0 -17
  67. data/spec/unit/oneview_power_spec.rb +0 -51
  68. data/spec/unit/oneview_san_spec.rb +0 -86
  69. data/spec/unit/oneview_search_spec.rb +0 -63
  70. data/spec/unit/rest_api_spec.rb +0 -115
@@ -1,64 +1,66 @@
1
- module CustomizeMachine
2
- private
1
+ module OneviewChefProvisioningDriver
2
+ # Handles OS install and network configuration
3
+ module CustomizeMachine
4
+ # Use ICSP to install OS
5
+ def customize_machine(action_handler, machine_spec, machine_options, profile)
6
+ machine_name = machine_spec.name
7
+ # Wait for server profile to finish building
8
+ wait_for_profile(action_handler, machine_name, profile)
9
+ profile.refresh
3
10
 
4
- # Use ICSP to install OS
5
- def customize_machine(action_handler, machine_spec, machine_options, profile)
6
- # Wait for server profile to finish building
7
- wait_for_profile(action_handler, machine_spec, machine_options, profile)
11
+ # Configure SAN storage (if applicable)
12
+ enable_boot_from_san(action_handler, machine_name, profile) unless machine_spec.reference['network_personalitation_finished']
8
13
 
9
- # Configure SAN storage (if applicable)
10
- enable_boot_from_san(action_handler, machine_spec, profile) unless machine_spec.reference['network_personalitation_finished']
14
+ # Make sure server is started
15
+ profile.get_server_hardware.power_on
11
16
 
12
- # Make sure server is started
13
- power_on(action_handler, machine_spec, profile['serverHardwareUri'])
17
+ return if @icsp_ignore == true
14
18
 
15
- # Get ICSP servers to poll and wait until server PXE complete (to make sure ICSP is available).
16
- my_server = nil
17
- action_handler.perform_action "Wait for #{machine_spec.name} to boot into HP Intelligent Provisioning" do
18
- action_handler.report_progress "INFO: Waiting for #{machine_spec.name} to PXE boot into HP Intelligent Provisioning"
19
- 360.times do # Wait for up to 1 hr
20
- my_server = get_icsp_server_by_sn(profile['serialNumber'])
21
- break if !my_server.nil?
22
- print '.'
23
- sleep 10
19
+ # Get ICSP servers to poll and wait until server PXE complete (to make sure ICSP is available).
20
+ my_server = nil
21
+ action_handler.perform_action "Wait for #{machine_name} to boot into HP Intelligent Provisioning" do
22
+ action_handler.report_progress "INFO: Waiting for #{machine_name} to PXE boot into HP Intelligent Provisioning"
23
+ 360.times do # Wait for up to 1 hr
24
+ my_server = get_icsp_server_by_sn(profile['serialNumber'])
25
+ break unless my_server.nil?
26
+ print '.'
27
+ sleep 10
28
+ end
29
+ raise "Timeout waiting for server #{machine_name} to register with ICSP" if my_server.nil?
24
30
  end
25
- raise "Timeout waiting for server #{machine_spec.name} to register with ICSP" if my_server.nil?
26
- end
27
31
 
28
- icsp_configure_nic_teams(machine_options, profile)
32
+ icsp_configure_nic_teams(machine_options, profile)
29
33
 
30
- icsp_set_custom_attributes(machine_options, my_server)
34
+ icsp_set_custom_attributes(machine_options, my_server)
31
35
 
32
- icsp_run_os_install(action_handler, machine_spec, machine_options, my_server, profile)
36
+ icsp_run_os_install(action_handler, machine_spec, machine_options, my_server, profile)
33
37
 
34
- # Customize networking
35
- if !machine_spec.reference['network_personalitation_finished'] || machine_options[:driver_options][:force_network_update]
38
+ # Customize networking
39
+ if !machine_spec.reference['network_personalitation_finished'] || machine_options[:driver_options][:force_network_update]
36
40
 
37
- icsp_configure_networking(action_handler, machine_spec, machine_options, my_server, profile)
41
+ icsp_configure_networking(action_handler, machine_spec, machine_options, my_server, profile)
38
42
 
39
- # Switch deploy networks to post-deploy networks if specified
40
- if machine_options[:driver_options][:connections]
41
- available_networks = rest_api(:oneview, :get, "/rest/server-profiles/available-networks?serverHardwareTypeUri=#{profile['serverHardwareTypeUri']}&enclosureGroupUri=#{profile['enclosureGroupUri']}")
42
- machine_options[:driver_options][:connections].each do |id, data|
43
- next unless data && data[:net] && data[:deployNet]
44
- action_handler.report_progress "INFO: Performing network flipping on #{machine_spec.name}, connection #{id}"
45
- deploy_network = available_networks['ethernetNetworks'].find {|n| n['name'] == data[:deployNet] }
46
- new_network = available_networks['ethernetNetworks'].find {|n| n['name'] == data[:net] }
47
- raise "Failed to perform network flipping on #{machine_spec.name}, connection #{id}. '#{data[:net]}' network not found" if new_network.nil?
48
- raise "Failed to perform network flipping on #{machine_spec.name}, connection #{id}. '#{data[:deployNet]}' network not found" if deploy_network.nil?
49
- profile = get_oneview_profile_by_sn(machine_spec.reference['serial_number'])
50
- profile['connections'].find {|c| c['networkUri'] == deploy_network['uri'] }['networkUri'] = new_network['uri']
51
- options = { 'body' => profile }
52
- task = rest_api(:oneview, :put, profile['uri'], options)
53
- raise "Failed to perform network flipping on #{machine_spec.name}. Details: #{task['message'] || task}" unless task['uri']
54
- task = oneview_wait_for(task['uri']) # Wait up to 10 min
55
- raise "Timed out waiting for network flipping on #{machine_spec.name}" if task == false
56
- raise "Error performing network flip on #{machine_spec.name}. Response: #{task}" unless task == true
43
+ # Switch deploy networks to post-deploy networks if specified
44
+ if machine_options[:driver_options][:connections]
45
+ available_networks = profile.available_networks
46
+ machine_options[:driver_options][:connections].each do |id, data|
47
+ next unless data && data[:net] && data[:deployNet]
48
+ action_handler.perform_action "Perform network flipping on #{machine_name}, connection #{id}" do
49
+ action_handler.report_progress "INFO: Performing network flipping on #{machine_name}, connection #{id}"
50
+ deploy_network = available_networks['ethernetNetworks'].find { |n| n['name'] == data[:deployNet] }
51
+ new_network = available_networks['ethernetNetworks'].find { |n| n['name'] == data[:net] }
52
+ raise "Failed to perform network flipping on #{machine_name}, connection #{id}. '#{data[:net]}' network not found" if new_network.nil?
53
+ raise "Failed to perform network flipping on #{machine_name}, connection #{id}. '#{data[:deployNet]}' network not found" if deploy_network.nil?
54
+ profile.refresh
55
+ profile['connections'].find { |c| c['networkUri'] == deploy_network['uri'] }['networkUri'] = new_network['uri']
56
+ profile.update
57
+ end
58
+ end
57
59
  end
60
+ machine_spec.reference['network_personalitation_finished'] = true
58
61
  end
59
- machine_spec.reference['network_personalitation_finished'] = true
60
- end
61
62
 
62
- my_server = rest_api(:icsp, :get, my_server['uri'])
63
+ my_server = rest_api(:icsp, :get, my_server['uri'])
64
+ end
63
65
  end
64
66
  end
@@ -0,0 +1,18 @@
1
+ require_relative 'create_machine'
2
+ require_relative 'customize_machine'
3
+ require_relative 'oneview/oneview_helper'
4
+ require_relative 'oneview/san_storage'
5
+ require_relative 'icsp/icsp_helper'
6
+
7
+ module OneviewChefProvisioningDriver
8
+ # Module that just includes all other helper modules
9
+ module Helpers
10
+ include CreateMachine # Handles allocation of OneView ServerProfile
11
+ include CustomizeMachine # Handles OS install and network configuration
12
+
13
+ include OneViewHelper # Helpers for OneView actions
14
+ include OneViewSanStorage # Helpers for OneView SAN storage actions
15
+
16
+ include IcspHelper # Helpers for ICSP actions
17
+ end
18
+ end
@@ -1,3 +1,4 @@
1
+ # Helper module for ICSP v104 API requests
1
2
  module ICspAPIv104
2
3
  # Parse and clean connection data for api call
3
4
  def icsp_v104_parse_connection(machine_options, c)
@@ -11,14 +12,14 @@ module ICspAPIv104
11
12
  c[:dnsServers] = nil if c[:dnsServers] == :none
12
13
  c[:dnsServers] = c[:dnsServers].split(',') if c[:dnsServers].class == String
13
14
  c[:staticNetworks] ||= ["#{c[:ip4Address]}/#{c[:mask] || machine_options[:driver_options][:mask] || '24'}"] if c[:ip4Address]
14
- c.keep_if {|k, _v| allowed_keys.include? k.to_s }
15
+ c.keep_if { |k, _v| allowed_keys.include? k.to_s }
15
16
  end
16
17
 
17
18
  # Parse and clean personality_data data for api call
18
19
  def icsp_v104_build_personality_data(machine_options, nics)
19
20
  allowed_keys = %w(hostname domain workgroup)
20
21
  personality_data = Marshal.load(Marshal.dump(machine_options[:driver_options])) || {}
21
- personality_data.keep_if {|k, _v| allowed_keys.include? k.to_s }
22
+ personality_data.keep_if { |k, _v| allowed_keys.include? k.to_s }
22
23
  personality_data['hostname'] ||= machine_options[:driver_options][:host_name]
23
24
  personality_data['domain'] ||= machine_options[:driver_options][:domainName]
24
25
  personality_data['interfaces'] = nics
@@ -0,0 +1,288 @@
1
+ require_relative 'rest'
2
+ require_relative 'api_v104'
3
+
4
+ module OneviewChefProvisioningDriver
5
+ # Helpers for ICSP actions
6
+ module IcspHelper
7
+ include RestAPI
8
+ include ICspAPIv104
9
+
10
+ def get_icsp_api_version
11
+ begin
12
+ version = rest_api(:icsp, :get, '/rest/version', 'Content-Type' => :none, 'X-API-Version' => :none, 'auth' => :none)['currentVersion']
13
+ raise "Couldn't get API version" unless version
14
+ if version.class != Fixnum
15
+ version = version.to_i
16
+ raise 'API version type mismatch' if !version > 0
17
+ end
18
+ rescue
19
+ puts 'Failed to get ICSP API version. Setting to default (102)'
20
+ version = 102
21
+ end
22
+ version
23
+ end
24
+
25
+ def login_to_icsp
26
+ path = '/rest/login-sessions'
27
+ options = {
28
+ 'body' => {
29
+ 'userName' => @icsp_username,
30
+ 'password' => @icsp_password,
31
+ 'authLoginDomain' => 'LOCAL'
32
+ }
33
+ }
34
+ response = rest_api(:icsp, :post, path, options)
35
+ return response['sessionID'] if response['sessionID']
36
+ raise("\nERROR! Couldn't log into ICSP server at #{@icsp_base_url}. Response:\n#{response}")
37
+ end
38
+
39
+ def get_icsp_server_by_sn(serial_number)
40
+ raise 'Must specify a serialNumber!' if serial_number.nil? || serial_number.empty?
41
+ search_result = rest_api(:icsp, :get,
42
+ "/rest/index/resources?category=osdserver&query='osdServerSerialNumber:\"#{serial_number}\"'")['members'] rescue nil
43
+ if search_result && search_result.size == 1 && search_result.first['attributes']['osdServerSerialNumber'] == serial_number
44
+ my_server_uri = search_result.first['uri']
45
+ my_server = rest_api(:icsp, :get, my_server_uri)
46
+ end
47
+ unless my_server && my_server['uri']
48
+ os_deployment_servers = rest_api(:icsp, :get, '/rest/os-deployment-servers')
49
+ # Pick the relevant os deployment server from icsp
50
+ my_server = nil
51
+ os_deployment_servers['members'].each do |server|
52
+ if server['serialNumber'] == serial_number
53
+ my_server = server
54
+ break
55
+ end
56
+ end
57
+ end
58
+ my_server
59
+ end
60
+
61
+ def icsp_wait_for(task_uri, wait_iterations = 60, sleep_seconds = 10)
62
+ raise 'Must specify a task_uri!' if task_uri.nil? || task_uri.empty?
63
+ wait_iterations.times do
64
+ task = rest_api(:icsp, :get, task_uri)
65
+ if task['taskState']
66
+ case task['taskState'].downcase
67
+ when 'completed'
68
+ return true
69
+ when 'error', 'killed', 'terminated'
70
+ return task
71
+ end
72
+ elsif task['running'] == 'false' && task['jobResult']
73
+ if task['state'] == 'STATUS_SUCCESS'
74
+ return true
75
+ else
76
+ return task
77
+ end
78
+ end
79
+ print '.'
80
+ sleep sleep_seconds
81
+ end
82
+ false
83
+ end
84
+
85
+ # Consume and set any custom attributes that were specified
86
+ def icsp_set_custom_attributes(machine_options, my_server)
87
+ if machine_options[:driver_options][:custom_attributes]
88
+ curr_server = rest_api(:icsp, :get, my_server['uri'])
89
+ machine_options[:driver_options][:custom_attributes].each do |key, val|
90
+ curr_server['customAttributes'].push(
91
+ 'values' => [{ 'scope' => 'server', 'value' => val.to_s }],
92
+ 'key' => key.to_s
93
+ )
94
+ end
95
+ options = { 'body' => curr_server }
96
+ rest_api(:icsp, :put, my_server['uri'], options)
97
+ end
98
+ end
99
+
100
+ def icsp_run_os_install(action_handler, machine_spec, machine_options, my_server, profile)
101
+ return if my_server['state'] == 'OK' # Skip if the OS has already been deployed
102
+
103
+ # Wait for my_server['state'] to be in MAINTENANCE mode
104
+ if my_server['state'] != 'MAINTENANCE'
105
+ action_handler.perform_action "Wait for #{machine_spec.name} to go into maintenance mode in ICsp" do
106
+ action_handler.report_progress "INFO: Waiting for #{machine_spec.name} to go into maintenance mode in ICsp"
107
+ 120.times do # Wait for up to 20 min
108
+ my_server = get_icsp_server_by_sn(profile['serialNumber'])
109
+ break if my_server['state'] != 'MAINTENANCE'
110
+ print '.'
111
+ sleep 10
112
+ end
113
+ raise "Timed out waiting for #{machine_spec.name} to go into maintenance mode in ICsp. State: #{my_server['state']}" unless my_server['state'] == 'MAINTENANCE'
114
+ end
115
+ end
116
+
117
+ # Get the specified OS Build Plan(s)
118
+ os_builds = machine_options[:driver_options][:os_build]
119
+ os_builds = [os_builds] if os_builds.class == String
120
+ build_plan_uris = []
121
+ action_handler.perform_action "Get OS Build Plan(s) info for #{machine_spec.name}" do
122
+ action_handler.report_progress "INFO: Getting OS Build Plan(s) for #{machine_spec.name}"
123
+ os_builds.each do |os_build|
124
+ uri = "/rest/index/resources?userQuery=\"'#{os_build}'\"&category=osdbuildplan"
125
+ while uri
126
+ matching_plans = rest_api(:icsp, :get, uri)
127
+ raise "Search failed for OSBP '#{os_build}'. Response: #{matching_plans}" unless matching_plans['members']
128
+ build_plan_uri = matching_plans['members'].find { |bp| bp['name'] == os_build }['uri'] rescue nil
129
+ break unless build_plan_uri.nil?
130
+ uri = URI.unescape(matching_plans['nextPageUri']) rescue nil
131
+ end
132
+ raise "OS build plan #{os_build} not found!" if build_plan_uri.nil?
133
+ build_plan_uris.push build_plan_uri
134
+ end
135
+ end
136
+
137
+ # Build options for the OS deployment
138
+ options = {}
139
+ options['X-API-Version'] = 104 if @current_icsp_api_version.between?(104, 108)
140
+ options['body'] = {
141
+ 'osbpUris' => build_plan_uris,
142
+ 'serverData' => [{
143
+ 'serverUri' => my_server['uri']
144
+ }]
145
+ }
146
+
147
+ # Do the OS deployment
148
+ action_handler.perform_action "Run: #{os_builds} OS Build Plan(s) on #{machine_spec.name}" do
149
+ action_handler.report_progress "INFO: Running: #{os_builds} OS Build Plan(s) on #{machine_spec.name}"
150
+ task = rest_api(:icsp, :post, '/rest/os-deployment-jobs/?force=true', options)
151
+ task_uri = task['uri']
152
+ raise "Failed to start OS Deployment Job. Details: #{task['details'] || task['message'] || task}" unless task_uri
153
+ task = icsp_wait_for(task_uri, 720)
154
+ raise "Error running OS build plan(s) #{os_builds}: #{task['jobResult'].first['jobMessage']}\n#{task['jobResult'].first['jobResultErrorDetails']}" unless task == true
155
+ end
156
+ end
157
+
158
+ def icsp_configure_networking(action_handler, machine_spec, machine_options, my_server, profile)
159
+ return if machine_options[:driver_options][:skip_network_configuration] == true
160
+ action_handler.perform_action "Configure networking on #{machine_spec.name}" do
161
+ action_handler.report_progress "INFO: Configuring networking on #{machine_spec.name}"
162
+ personality_data = icsp_build_personality_data(machine_options, profile)
163
+ options = {}
164
+ options['X-API-Version'] = 104 if @current_icsp_api_version.between?(104, 108)
165
+ options['body'] = {
166
+ 'serverData' => [{
167
+ 'serverUri' => my_server['uri'],
168
+ 'personalityData' => personality_data
169
+ }]
170
+ }
171
+
172
+ task = rest_api(:icsp, :post, '/rest/os-deployment-jobs/?force=true', options)
173
+ task_uri = task['uri']
174
+ raise "Failed to start network personalization job. Details: #{task['details'] || task['message'] || task}" unless task_uri
175
+ task = icsp_wait_for(task_uri, 60) # Wait for up to 10 min
176
+ raise "Error running network personalization job: #{task['jobResult'].first['jobMessage']}\n#{task['jobResult'].first['jobResultErrorDetails']}" unless task == true
177
+
178
+ # Check if ICsp IP config matches machine options
179
+ requested_ips = []
180
+ machine_options[:driver_options][:connections].each do |_id, c|
181
+ requested_ips.push c[:ip4Address] if c[:ip4Address] && c[:dhcp] == false
182
+ end
183
+ my_server_connections = []
184
+ 10.times do
185
+ my_server_connections = rest_api(:icsp, :get, my_server['uri'])['interfaces']
186
+ my_server_connections.each { |c| requested_ips.delete c['ipv4Addr'] }
187
+ break if requested_ips.empty?
188
+ print ','
189
+ sleep 10
190
+ end
191
+ puts "\nWARN: The following IPs are not visible on ICsp, so they may not have gotten configured correctly: #{requested_ips}" unless requested_ips.empty?
192
+
193
+ # Set interface data as normal node attributes
194
+ my_server_connections.each do |c|
195
+ c['oneViewId'] = profile['connections'].find { |x| x['mac'] == c['macAddr'] }['id'] rescue nil
196
+ end
197
+ machine_spec.data['normal']['icsp'] ||= {}
198
+ machine_spec.data['normal']['icsp']['interfaces'] = my_server_connections
199
+ end
200
+ end
201
+
202
+ # Build options for the network configuration
203
+ def icsp_build_personality_data(machine_options, profile)
204
+ nics = []
205
+ if machine_options[:driver_options][:connections]
206
+ machine_options[:driver_options][:connections].each do |id, data|
207
+ c = Marshal.load(Marshal.dump(data))
208
+ next unless c[:dhcp] || c[:dhcpv4] || c[:ip4Address] || c[:ipv6autoconfig] || c[:staticNetworks] # Invalid network or only switch networks specified
209
+ c.delete_if { |k, _v| k.to_s == 'bootstrap' }
210
+ begin
211
+ c[:macAddress] = profile['connections'].find { |x| x['id'] == id }['mac']
212
+ rescue NoMethodError
213
+ ids = []
214
+ profile['connections'].each { |x| ids.push x['id'] }
215
+ raise "Could not find connection id #{id} for #{profile['name']}. Available connection ids are: #{ids}. Please make sure the connection ids map to those on OneView."
216
+ end
217
+ if @current_icsp_api_version.between?(104, 108)
218
+ icsp_v104_parse_connection(machine_options, c)
219
+ else
220
+ c[:mask] ||= machine_options[:driver_options][:mask]
221
+ c[:dhcp] ||= false
222
+ c[:gateway] ||= machine_options[:driver_options][:gateway]
223
+ c[:dns] ||= machine_options[:driver_options][:dns]
224
+ end
225
+ nics.push c
226
+ end
227
+ end
228
+
229
+ if @current_icsp_api_version.between?(104, 108)
230
+ personality_data = icsp_v104_build_personality_data(machine_options, nics)
231
+ else
232
+ personality_data = {
233
+ 'hostName' => machine_options[:driver_options][:host_name],
234
+ 'domainType' => machine_options[:driver_options][:domainType],
235
+ 'domainName' => machine_options[:driver_options][:domainName],
236
+ 'nics' => nics
237
+ }
238
+ end
239
+ personality_data
240
+ end
241
+
242
+ def destroy_icsp_server(action_handler, machine_spec)
243
+ my_server = get_icsp_server_by_sn(machine_spec.reference['serial_number'])
244
+ return false if my_server.nil? || my_server['uri'].nil?
245
+
246
+ action_handler.perform_action "Delete server #{machine_spec.name} from ICSP" do
247
+ task = rest_api(:icsp, :delete, my_server['uri']) # TODO: This returns nil instead of task info
248
+ if task['uri']
249
+ task_uri = task['uri']
250
+ 90.times do # Wait for up to 15 minutes
251
+ task = rest_api(:icsp, :get, task_uri)
252
+ break if task['taskState'].casecmp('completed') == 0
253
+ print '.'
254
+ sleep 10
255
+ end
256
+ raise "Deleting os deployment server #{machine_spec.name} at icsp failed!" unless task['taskState'].casecmp('completed') == 0
257
+ end
258
+ end
259
+ end
260
+
261
+ # Sets ICSP custom attributes to enable nic teaming
262
+ def icsp_configure_nic_teams(machine_options, profile)
263
+ return false if machine_options[:driver_options][:connections].nil?
264
+ teams = {}
265
+
266
+ machine_options[:driver_options][:connections].each do |id, options|
267
+ next unless options.is_a?(Hash) && options[:team]
268
+ raise "#{options[:team]}: Team names must not include hyphens" if options[:team].to_s.include?('-')
269
+ teams[options[:team].to_s] ||= []
270
+ begin
271
+ mac = profile['connections'].find { |x| x['id'] == id }['mac']
272
+ teams[options[:team].to_s].push mac
273
+ rescue NoMethodError
274
+ ids = []
275
+ profile['connections'].each { |x| ids.push x['id'] }
276
+ raise "Failed to configure nic teams: Could not find connection id #{id} for #{profile['name']}. Available connection ids are: #{ids}. Please make sure the connection ids map to those on OneView."
277
+ end
278
+ end
279
+ team_strings = []
280
+ teams.each do |name, macs|
281
+ raise "Team '#{name}' must have at least 2 associated connections to form a NIC team" unless macs.size >= 2
282
+ team_strings.push "#{name}-#{macs.join(',')}"
283
+ end
284
+ machine_options[:driver_options][:custom_attributes] ||= {}
285
+ machine_options[:driver_options][:custom_attributes][:teams] = team_strings.join('|')
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,50 @@
1
+ module OneviewChefProvisioningDriver
2
+ # Helpers for REST API calls
3
+ module RestAPI
4
+ # API calls for OneView and ICsp
5
+ def rest_api(host, type, path, options = {})
6
+ disable_ssl = false
7
+ case host
8
+ when 'icsp', :icsp
9
+ uri = URI.parse(URI.escape(@icsp_base_url + path))
10
+ options['X-API-Version'] ||= @icsp_api_version unless [:put, 'put'].include?(type.downcase)
11
+ options['auth'] ||= @icsp_key
12
+ disable_ssl = true if @icsp_disable_ssl
13
+ else
14
+ raise "Invalid rest host: #{host}"
15
+ end
16
+
17
+ http = Net::HTTP.new(uri.host, uri.port)
18
+ http.read_timeout = @icsp_timeout if @icsp_timeout # timeout for a request
19
+ http.open_timeout = @icsp_timeout if @icsp_timeout # timeout for a connection
20
+ http.use_ssl = true if uri.scheme == 'https'
21
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if disable_ssl
22
+ case type.downcase
23
+ when 'get', :get
24
+ request = Net::HTTP::Get.new(uri.request_uri)
25
+ when 'post', :post
26
+ request = Net::HTTP::Post.new(uri.request_uri)
27
+ when 'put', :put
28
+ request = Net::HTTP::Put.new(uri.request_uri)
29
+ when 'delete', :delete
30
+ request = Net::HTTP::Delete.new(uri.request_uri)
31
+ else
32
+ raise "Invalid rest call: #{type}"
33
+ end
34
+ options['Content-Type'] ||= 'application/json'
35
+ options.delete('Content-Type') if [:none, 'none', nil].include?(options['Content-Type'])
36
+ options.delete('X-API-Version') if [:none, 'none', nil].include?(options['X-API-Version'])
37
+ options.delete('auth') if [:none, 'none', nil].include?(options['auth'])
38
+ options.each do |key, val|
39
+ if key.casecmp('body') == 0
40
+ request.body = val.to_json rescue val
41
+ else
42
+ request[key] = val
43
+ end
44
+ end
45
+
46
+ response = http.request(request)
47
+ JSON.parse(response.body) rescue response
48
+ end
49
+ end
50
+ end