chef-provisioning-oneview 1.2.1 → 1.3.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 (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