chef-provisioning-oneview 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +43 -3
- data/lib/chef/provisioning/create_machine.rb +52 -0
- data/lib/chef/provisioning/customize_machine.rb +78 -0
- data/lib/chef/provisioning/icsp/api_v104.rb +27 -0
- data/lib/chef/provisioning/icsp/icsp_api.rb +282 -0
- data/lib/chef/provisioning/oneview/oneview_api.rb +56 -357
- data/lib/chef/provisioning/oneview/san_storage.rb +91 -0
- data/lib/chef/provisioning/oneview/v1.2/api.rb +3 -0
- data/lib/chef/provisioning/oneview_driver.rb +42 -5
- data/lib/chef/provisioning/rest.rb +51 -0
- data/lib/chef/provisioning/{oneview/version.rb → version.rb} +1 -1
- data/spec/shared_context.rb +77 -0
- data/spec/spec_helper.rb +8 -6
- data/spec/{unit/support → support}/fake_action_handler.rb +0 -0
- data/spec/support/fake_icsp.rb +73 -21
- data/spec/support/fake_machine_spec.rb +18 -0
- data/spec/support/fake_oneview.rb +148 -21
- data/spec/support/fixtures/icsp/v102/error_404.json +13 -13
- data/spec/support/fixtures/icsp/v102/login.json +4 -4
- data/spec/support/fixtures/icsp/v102/os-deployment-build-plans.json +99 -99
- data/spec/support/fixtures/icsp/v102/{os-deployment-servers_managed.json → os-deployment-servers.json} +178 -178
- data/spec/support/fixtures/icsp/v102/os-deployment-servers_1670001.json +83 -0
- data/spec/support/fixtures/icsp/v102/os-deployment-servers_fakesn.json +9 -0
- data/spec/support/fixtures/icsp/v102/{server_by_sn.json → server_by_sn_VCGE9KB041.json} +44 -44
- data/spec/support/fixtures/icsp/v102/server_by_sn_empty.json +16 -0
- data/spec/support/fixtures/icsp/v102/version.json +3 -3
- data/spec/support/fixtures/oneview/v120/error_404.json +13 -13
- data/spec/support/fixtures/oneview/v120/login.json +4 -4
- data/spec/support/fixtures/oneview/v120/server-hardware.json +1475 -1475
- data/spec/support/fixtures/oneview/v120/server-hardware_Template-WebServer.json +468 -0
- data/spec/support/fixtures/oneview/v120/server-hardware_specific.json +151 -0
- data/spec/support/fixtures/oneview/v120/server-profiles.json +368 -746
- data/spec/support/fixtures/oneview/v120/server-profiles_invalid_filter.json +14 -0
- data/spec/support/fixtures/oneview/v120/{server-profiles_specific.json → server-profiles_name_Template-WebServer.json} +132 -132
- data/spec/support/fixtures/oneview/v120/server-profiles_name_Template-WebServerWithSAN.json +200 -0
- data/spec/support/fixtures/oneview/v120/server-profiles_name_chef-web01.json +133 -0
- data/spec/support/fixtures/oneview/v120/server-profiles_name_chef-web03.json +133 -0
- data/spec/support/fixtures/oneview/v120/server-profiles_name_empty.json +15 -0
- data/spec/support/fixtures/oneview/v120/server-profiles_sn_VCGE9KB041.json +133 -0
- data/spec/support/fixtures/oneview/v120/server-profiles_sn_VCGE9KB042.json +206 -0
- data/spec/support/fixtures/oneview/v120/server-profiles_sn_empty.json +15 -0
- data/spec/support/fixtures/oneview/v120/storage-volumes_1B5D3CA2-6C5B-41C2-8B97-1821F1883F22.json +26 -0
- data/spec/support/fixtures/oneview/v120/tasks_fake_active.json +5 -0
- data/spec/support/fixtures/oneview/v120/tasks_fake_complete.json +5 -0
- data/spec/support/fixtures/oneview/v120/version.json +3 -3
- data/spec/support/fixtures/oneview/v200/server-profile-templates_WebServerTemplate.json +109 -0
- data/spec/support/fixtures/oneview/v200/server-profile-templates_WebServerTemplateWithSAN.json +144 -0
- data/spec/support/fixtures/oneview/v200/server-profile-templates_invalid.json +16 -0
- data/spec/support/fixtures/oneview/v200/server-profile-templates_new-profile_WebServerTemplate.json +125 -0
- data/spec/support/fixtures/oneview/v200/server-profile-templates_new-profile_WebServerTemplateWithSAN.json +178 -0
- data/spec/support/fixtures/oneview/v200/version.json +4 -0
- data/spec/unit/create_machine_spec.rb +78 -0
- data/spec/unit/destroy_spec.rb +26 -0
- data/spec/unit/icsp_nic_teams_spec.rb +38 -0
- data/spec/unit/icsp_search_spec.rb +25 -0
- data/spec/unit/oneview_driver_spec.rb +37 -64
- data/spec/unit/oneview_login_spec.rb +23 -0
- data/spec/unit/oneview_power_spec.rb +51 -0
- data/spec/unit/oneview_san_spec.rb +86 -0
- data/spec/unit/oneview_search_spec.rb +63 -0
- data/spec/unit/rest_api_spec.rb +115 -0
- metadata +90 -9
- data/lib/chef/provisioning/oneview/v1.20/api.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c3667ee64a73be8e4461ef96c4809810bea95e8
|
4
|
+
data.tar.gz: b1e38d4a95c2b93d25c665cec20e1ec9080557bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cf35136389e6bb5dcffced8755ae54929f7bb35e9cea7d6501ed9d5ef1f457640e31d5cb6f6d831d7b859c6947a28118f2f856142884b539b73c04ad57e4c2d
|
7
|
+
data.tar.gz: 421361c479b72770676bd397e8591fe979c4d8b809813233522b059b01ab538dfa6e081b956ed67d229e3fbd1465b7b46c9b6601f877296d5456b04fba74d355
|
data/README.md
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
# chef-provisioning-oneview
|
2
2
|
Chef Provisioning driver for HP OneView
|
3
3
|
|
4
|
-
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/HewlettPackard/chef-provisioning-oneview.svg?branch=master)](https://travis-ci.org/HewlettPackard/chef-provisioning-oneview)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/chef-provisioning-oneview.svg)](https://badge.fury.io/rb/chef-provisioning-oneview)
|
7
|
+
|
8
|
+
Questions or comments? Join the Gitter room [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/HewlettPackard/chef-provisioning-oneview?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
9
|
+
|
10
|
+
|
11
|
+
Currently supports:
|
12
|
+
- OneView v1.2.0 - 2.0.0
|
13
|
+
- ICsp v7.4.0 - 7.5.0
|
5
14
|
|
6
15
|
# Installation
|
7
16
|
|
@@ -40,7 +49,7 @@ Currently supports OneView v1.2.0 and ICsp v7.4.0
|
|
40
49
|
```
|
41
50
|
|
42
51
|
- Your OneView, Insight Controll Server Provisioning(ICSP), and Chef server must be trusted by your certificate stores. See [examples/ssl_issues.md](examples/ssl_issues.md) for more info on how to do this.
|
43
|
-
- Your OneView and ICSP servers must be set up beforehand. Unfortunately, this driver doesn't do that for you too.
|
52
|
+
- Your OneView and ICSP servers must be set up beforehand. Unfortunately, this driver doesn't do that for you too.
|
44
53
|
|
45
54
|
# Usage
|
46
55
|
|
@@ -61,7 +70,7 @@ machine 'web01' do
|
|
61
70
|
:server_template => 'Web Server Template',
|
62
71
|
:os_build => 'CHEF-RHEL-6.5-x64',
|
63
72
|
:host_name => 'chef-web01',
|
64
|
-
:ip_address => 'xx.xx.xx.xx', # For bootstrapping
|
73
|
+
:ip_address => 'xx.xx.xx.xx', # For bootstrapping. Deprecated in favor of { bootstrap: true } in connection; see below
|
65
74
|
|
66
75
|
:domainType => 'workgroup',
|
67
76
|
:domainName => 'sub.domain.com',
|
@@ -77,6 +86,12 @@ machine 'web01' do
|
|
77
86
|
:dhcp => false # Optional. Overrides dhcp property above
|
78
87
|
:gateway => 'xx.xx.xx.1' # Optional. Overrides gateway property above
|
79
88
|
:dns => 'xx.xx.xx.xx' # Optional. Overrides dns property above
|
89
|
+
:bootstrap => true # Set this on 1 connection only. Tells Chef which connection to use to bootstrap.
|
90
|
+
},
|
91
|
+
3 => {
|
92
|
+
:dhcp => true # Optional. Overrides dhcp property above
|
93
|
+
:gateway => :none # Optional. Overrides gateway property above
|
94
|
+
:dns => :none # Optional. Overrides dns property above
|
80
95
|
}
|
81
96
|
},
|
82
97
|
:custom_attributes => {
|
@@ -101,6 +116,8 @@ end
|
|
101
116
|
|
102
117
|
See https://github.com/chef/chef-provisioning-ssh for more transport_options.
|
103
118
|
|
119
|
+
NOTE: Some basic connection settings such as :ip4Address and :dhcp are shown in the example recipe, but you can pass in any interface/nic options that exist in the ICsp api for POST requests to /rest/os-deployment-jobs
|
120
|
+
|
104
121
|
### Custom Attributes
|
105
122
|
Insided the custom attributes hash, you can specify any data that you would like to pass into your ICsp build plan scripts or configuration files. For example, to specify a list of trusted public keys to be placed into the node's .ssh/authorized_keys file, add a custom attribute to the machine resource definition:
|
106
123
|
|
@@ -120,10 +137,33 @@ if [ -n "$authorized_keys"]; then
|
|
120
137
|
fi
|
121
138
|
```
|
122
139
|
|
140
|
+
### SSH Keys
|
141
|
+
To use SSH keys insead of passwords to connect to nodes, you'll need to modify your transport_options to look something like:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
:transport_options => {
|
145
|
+
:ssh_options => {
|
146
|
+
:auth_methods => ['publickey'],
|
147
|
+
:keys => ['~/.ssh/id_rsa']
|
148
|
+
}
|
149
|
+
}
|
150
|
+
```
|
151
|
+
|
152
|
+
You'll also need to put the corresponding public key(s) into the node's authorized_keys file during the OS setup. See the Custom Attributes section above for one way to do this.
|
153
|
+
|
123
154
|
### Behind a proxy
|
124
155
|
Add `:bootstrap_proxy => 'http://proxy.domain.com:8080'` to your convergence_options hash.
|
125
156
|
Also, make sure your OS build plans set up the proxy configuration in a post OS install script.
|
126
157
|
|
158
|
+
### SAN Storage
|
159
|
+
In order to attach a SAN volume as a bootable volume, the volume name must start with 'boot'; it will be appended with the the profile name on creation.
|
160
|
+
|
161
|
+
### Switching to a different network after provisioning
|
162
|
+
Add `1 => {:net => "Deadnetwork", :deployNet => "PXE Network", :dhcp => true}` to your connections hash.
|
163
|
+
This will flip the first connection of the newly provisioned machine off of your pxe network to your Deadnetwork right after provisioning. This is helpful for taking the newly provisioned machine off the PXE network as soon as possible.
|
164
|
+
|
165
|
+
### Adding Nic Teams
|
166
|
+
Add `:team => 'TeamName'` into a connection in your connections hash. Make sure that you have 2 connections in a team and the name does not include hyphens. This information will be passed to ISCP as the 'teams' custom attribute in the format: `"TeamName1-mac1,mac2|TeamName2-mac6,mac7,mac8"` to be consumed in a custom build plan script.
|
127
167
|
|
128
168
|
# Doing a test run
|
129
169
|
This repo contains everything you need to get started, including example recipes and knife configuration files. See the README in the [examples](examples/) directory for how to begin provisioning.
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CreateMachine
|
2
|
+
private
|
3
|
+
|
4
|
+
# Chef oneview provisioning
|
5
|
+
def create_machine(action_handler, machine_spec, machine_options)
|
6
|
+
host_name = machine_options[:driver_options][:host_name]
|
7
|
+
|
8
|
+
auth_tokens # Login (to both ICSP and OneView)
|
9
|
+
|
10
|
+
# Check if profile exists first
|
11
|
+
matching_profiles = rest_api(:oneview, :get, "/rest/server-profiles?filter=name matches '#{host_name}'&sort=name:asc")
|
12
|
+
if matching_profiles['count'] > 0
|
13
|
+
profile = matching_profiles['members'].first
|
14
|
+
return profile
|
15
|
+
end
|
16
|
+
|
17
|
+
# Search for OneView Template by name
|
18
|
+
template = get_oneview_template(machine_options[:driver_options][:server_template])
|
19
|
+
|
20
|
+
# Get first availabe (and compatible) HP OV server blade
|
21
|
+
chosen_blade = available_hardware_for_template(template)
|
22
|
+
|
23
|
+
power_off(action_handler, machine_spec, chosen_blade['uri'])
|
24
|
+
|
25
|
+
# Create new profile instance from template
|
26
|
+
action_handler.perform_action "Initialize creation of server profile for #{machine_spec.name}" do
|
27
|
+
action_handler.report_progress "INFO: Initializing creation of server profile for #{machine_spec.name}"
|
28
|
+
|
29
|
+
# Add name & hardware uri to template
|
30
|
+
template['name'] = host_name
|
31
|
+
template['serverHardwareUri'] = chosen_blade['uri']
|
32
|
+
|
33
|
+
update_san_info(machine_spec, template)
|
34
|
+
|
35
|
+
# Post back to /rest/server-profiles
|
36
|
+
options = { 'body' => template }
|
37
|
+
options['X-API-Version'] = 200 if @current_oneview_api_version >= 200 && template['type'] == 'ServerProfileV5'
|
38
|
+
task = rest_api(:oneview, :post, '/rest/server-profiles', options)
|
39
|
+
task_uri = task['uri']
|
40
|
+
fail "Failed to create OneView server profile #{host_name}. Details: " unless task_uri
|
41
|
+
# Wait for profile to be created
|
42
|
+
60.times do # Wait for up to 5 min
|
43
|
+
matching_profiles = rest_api(:oneview, :get, "/rest/server-profiles?filter=name matches '#{host_name}'&sort=name:asc")
|
44
|
+
return matching_profiles['members'].first if matching_profiles['members'].first
|
45
|
+
print '.'
|
46
|
+
sleep 5
|
47
|
+
end
|
48
|
+
task = rest_api(:oneview, :get, task_uri)
|
49
|
+
fail "Server profile couldn't be created! #{task['taskStatus']}. #{task['taskErrors'].first['message']}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module CustomizeMachine
|
2
|
+
private
|
3
|
+
|
4
|
+
# Use ICSP to install OS
|
5
|
+
def customize_machine(action_handler, machine_spec, machine_options, profile)
|
6
|
+
auth_tokens # Login (to both ICSP and OneView)
|
7
|
+
|
8
|
+
# Wait for server profile to finish building
|
9
|
+
unless profile['state'] == 'Normal'
|
10
|
+
action_handler.perform_action "Wait for #{machine_spec.name} server to start and profile to be applied" do
|
11
|
+
action_handler.report_progress "INFO: Waiting for #{machine_spec.name} server to start and profile to be applied"
|
12
|
+
task = oneview_wait_for(profile['taskUri'], 240) # Wait up to 40 min for profile to be created
|
13
|
+
fail 'Timed out waiting for server to start and profile to be applied' if task == false
|
14
|
+
unless task == true
|
15
|
+
server_template = machine_options[:driver_options][:server_template]
|
16
|
+
fail "Error creating server profile from template #{server_template}: #{task['taskErrors'].first['message']}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
profile = get_oneview_profile_by_sn(machine_spec.reference['serial_number']) # Refresh profile
|
20
|
+
fail "Server profile state '#{profile['state']}' not 'Normal'" unless profile['state'] == 'Normal'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Configure SAN storage (if applicable)
|
24
|
+
enable_boot_from_san(action_handler, machine_spec, profile) unless machine_spec.reference['network_personalitation_finished']
|
25
|
+
|
26
|
+
# Make sure server is started
|
27
|
+
power_on(action_handler, machine_spec, profile['serverHardwareUri'])
|
28
|
+
|
29
|
+
# Get ICSP servers to poll and wait until server PXE complete (to make sure ICSP is available).
|
30
|
+
my_server = nil
|
31
|
+
action_handler.perform_action "Wait for #{machine_spec.name} to boot into HP Intelligent Provisioning" do
|
32
|
+
action_handler.report_progress "INFO: Waiting for #{machine_spec.name} to PXE boot into HP Intelligent Provisioning"
|
33
|
+
360.times do # Wait for up to 1 hr
|
34
|
+
my_server = get_icsp_server_by_sn(profile['serialNumber'])
|
35
|
+
break if !my_server.nil?
|
36
|
+
print '.'
|
37
|
+
sleep 10
|
38
|
+
end
|
39
|
+
fail "Timeout waiting for server #{machine_spec.name} to register with ICSP" if my_server.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
icsp_configure_nic_teams(machine_options, profile)
|
43
|
+
|
44
|
+
icsp_set_custom_attributes(machine_options, my_server)
|
45
|
+
|
46
|
+
icsp_run_os_install(action_handler, machine_spec, machine_options, my_server, profile)
|
47
|
+
|
48
|
+
# Customize networking
|
49
|
+
if !machine_spec.reference['network_personalitation_finished'] || machine_options[:driver_options][:force_network_update]
|
50
|
+
|
51
|
+
icsp_configure_networking(action_handler, machine_spec, machine_options, my_server, profile)
|
52
|
+
|
53
|
+
# Switch deploy networks to post-deploy networks if specified
|
54
|
+
if machine_options[:driver_options][:connections]
|
55
|
+
available_networks = rest_api(:oneview, :get, "/rest/server-profiles/available-networks?serverHardwareTypeUri=#{profile['serverHardwareTypeUri']}&enclosureGroupUri=#{profile['enclosureGroupUri']}")
|
56
|
+
machine_options[:driver_options][:connections].each do |id, data|
|
57
|
+
next unless data && data[:net] && data[:deployNet]
|
58
|
+
action_handler.report_progress "INFO: Performing network flipping on #{machine_spec.name}, connection #{id}"
|
59
|
+
deploy_network = available_networks['ethernetNetworks'].find {|n| n['name'] == data[:deployNet] }
|
60
|
+
new_network = available_networks['ethernetNetworks'].find {|n| n['name'] == data[:net] }
|
61
|
+
fail "Failed to perform network flipping on #{machine_spec.name}, connection #{id}. '#{data[:net]}' network not found" if new_network.nil?
|
62
|
+
fail "Failed to perform network flipping on #{machine_spec.name}, connection #{id}. '#{data[:deployNet]}' network not found" if deploy_network.nil?
|
63
|
+
profile = get_oneview_profile_by_sn(machine_spec.reference['serial_number'])
|
64
|
+
profile['connections'].find {|c| c['networkUri'] == deploy_network['uri'] }['networkUri'] = new_network['uri']
|
65
|
+
options = { 'body' => profile }
|
66
|
+
task = rest_api(:oneview, :put, profile['uri'], options)
|
67
|
+
fail "Failed to perform network flipping on #{machine_spec.name}. Details: #{task['message'] || task}" unless task['uri']
|
68
|
+
task = oneview_wait_for(task['uri']) # Wait up to 10 min
|
69
|
+
fail "Timed out waiting for network flipping on #{machine_spec.name}" if task == false
|
70
|
+
fail "Error performing network flip on #{machine_spec.name}. Response: #{task}" unless task == true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
machine_spec.reference['network_personalitation_finished'] = true
|
74
|
+
end
|
75
|
+
|
76
|
+
my_server = rest_api(:icsp, :get, my_server['uri'])
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ICspAPIv104
|
2
|
+
# Parse and clean connection data for api call
|
3
|
+
def icsp_v104_parse_connection(machine_options, c)
|
4
|
+
allowed_keys = %w(macAddress enabled dhcpv4 ipv6autoconfig provisioning dnsServers winsServers dnsSearch staticNetworks vlanid ipv4gateway ipv6gateway)
|
5
|
+
c[:enabled] ||= true
|
6
|
+
c[:vlanid] ||= '-1'
|
7
|
+
c[:dhcpv4] ||= c[:dhcp]
|
8
|
+
c[:ipv4gateway] ||= c[:gateway] || machine_options[:driver_options][:gateway]
|
9
|
+
c[:ipv4gateway] = nil if c[:ipv4gateway] == :none
|
10
|
+
c[:dnsServers] ||= c[:dns] || machine_options[:driver_options][:dns] || []
|
11
|
+
c[:dnsServers] = nil if c[:dnsServers] == :none
|
12
|
+
c[:dnsServers] = c[:dnsServers].split(',') if c[:dnsServers].class == String
|
13
|
+
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
|
+
end
|
16
|
+
|
17
|
+
# Parse and clean personality_data data for api call
|
18
|
+
def icsp_v104_build_personality_data(machine_options, nics)
|
19
|
+
allowed_keys = %w(hostname domain workgroup)
|
20
|
+
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['hostname'] ||= machine_options[:driver_options][:host_name]
|
23
|
+
personality_data['domain'] ||= machine_options[:driver_options][:domainName]
|
24
|
+
personality_data['interfaces'] = nics
|
25
|
+
personality_data
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,282 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/**/*.rb'].each {|file| require file } # Include all helper files in this directory & subdirectories
|
2
|
+
|
3
|
+
module ICspAPI
|
4
|
+
private
|
5
|
+
|
6
|
+
include ICspAPIv104
|
7
|
+
|
8
|
+
def get_icsp_api_version
|
9
|
+
begin
|
10
|
+
version = rest_api(:icsp, :get, '/rest/version', { 'Content-Type' => :none, 'X-API-Version' => :none, 'auth' => :none })['currentVersion']
|
11
|
+
fail "Couldn't get API version" unless version
|
12
|
+
if version.class != Fixnum
|
13
|
+
version = version.to_i
|
14
|
+
fail 'API version type mismatch' if !version > 0
|
15
|
+
end
|
16
|
+
rescue
|
17
|
+
puts 'Failed to get ICSP API version. Setting to default (102)'
|
18
|
+
version = 102
|
19
|
+
end
|
20
|
+
version
|
21
|
+
end
|
22
|
+
|
23
|
+
def login_to_icsp
|
24
|
+
path = '/rest/login-sessions'
|
25
|
+
options = {
|
26
|
+
'body' => {
|
27
|
+
'userName' => @icsp_username,
|
28
|
+
'password' => @icsp_password,
|
29
|
+
'authLoginDomain' => 'LOCAL'
|
30
|
+
}
|
31
|
+
}
|
32
|
+
response = rest_api(:icsp, :post, path, options)
|
33
|
+
return response['sessionID'] if response['sessionID']
|
34
|
+
fail("\nERROR! Couldn't log into OneView server at #{@oneview_base_url}. Response:\n#{response}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_icsp_server_by_sn(serialNumber)
|
38
|
+
fail 'Must specify a serialNumber!' if serialNumber.nil? || serialNumber.empty?
|
39
|
+
search_result = rest_api(:icsp, :get,
|
40
|
+
"/rest/index/resources?category=osdserver&query='osdServerSerialNumber:\"#{serialNumber}\"'")['members'] rescue nil
|
41
|
+
if search_result && search_result.size == 1 && search_result.first['attributes']['osdServerSerialNumber'] == serialNumber
|
42
|
+
my_server_uri = search_result.first['uri']
|
43
|
+
my_server = rest_api(:icsp, :get, my_server_uri)
|
44
|
+
end
|
45
|
+
unless my_server && my_server['uri']
|
46
|
+
os_deployment_servers = rest_api(:icsp, :get, '/rest/os-deployment-servers')
|
47
|
+
# Pick the relevant os deployment server from icsp
|
48
|
+
my_server = nil
|
49
|
+
os_deployment_servers['members'].each do |server|
|
50
|
+
if server['serialNumber'] == serialNumber
|
51
|
+
my_server = server
|
52
|
+
break
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
my_server
|
57
|
+
end
|
58
|
+
|
59
|
+
def icsp_wait_for(task_uri, wait_iterations = 60, sleep_seconds = 10)
|
60
|
+
fail 'Must specify a task_uri!' if task_uri.nil? || task_uri.empty?
|
61
|
+
wait_iterations.times do
|
62
|
+
task = rest_api(:icsp, :get, task_uri)
|
63
|
+
if task['taskState']
|
64
|
+
case task['taskState'].downcase
|
65
|
+
when 'completed'
|
66
|
+
return true
|
67
|
+
when 'error', 'killed', 'terminated'
|
68
|
+
return task
|
69
|
+
end
|
70
|
+
elsif task['running'] == 'false' && task['jobResult']
|
71
|
+
if task['state'] == 'STATUS_SUCCESS'
|
72
|
+
return true
|
73
|
+
else
|
74
|
+
return task
|
75
|
+
end
|
76
|
+
end
|
77
|
+
print '.'
|
78
|
+
sleep sleep_seconds
|
79
|
+
end
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
# Consume and set any custom attributes that were specified
|
84
|
+
def icsp_set_custom_attributes(machine_options, my_server)
|
85
|
+
if machine_options[:driver_options][:custom_attributes]
|
86
|
+
curr_server = rest_api(:icsp, :get, my_server['uri'])
|
87
|
+
machine_options[:driver_options][:custom_attributes].each do |key, val|
|
88
|
+
curr_server['customAttributes'].push({
|
89
|
+
'values' => [{ 'scope' => 'server', 'value' => val.to_s }],
|
90
|
+
'key' => key.to_s
|
91
|
+
})
|
92
|
+
end
|
93
|
+
options = { 'body' => curr_server }
|
94
|
+
rest_api(:icsp, :put, my_server['uri'], options)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def icsp_run_os_install(action_handler, machine_spec, machine_options, my_server, profile)
|
99
|
+
return if my_server['state'] == 'OK' # Skip if the OS has already been deployed
|
100
|
+
|
101
|
+
# Wait for my_server['state'] to be in MAINTENANCE mode
|
102
|
+
if my_server['state'] != 'MAINTENANCE'
|
103
|
+
action_handler.perform_action "Wait for #{machine_spec.name} to go into maintenance mode in ICsp" do
|
104
|
+
action_handler.report_progress "INFO: Waiting for #{machine_spec.name} to go into maintenance mode in ICsp"
|
105
|
+
120.times do # Wait for up to 20 min
|
106
|
+
my_server = get_icsp_server_by_sn(profile['serialNumber'])
|
107
|
+
break if my_server['state'] != 'MAINTENANCE'
|
108
|
+
print '.'
|
109
|
+
sleep 10
|
110
|
+
end
|
111
|
+
fail "Timed out waiting for #{machine_spec.name} to go into maintenance mode in ICsp. State: #{my_server['state']}" unless my_server['state'] == 'MAINTENANCE'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get the specified OS Build Plan(s)
|
116
|
+
os_builds = machine_options[:driver_options][:os_build]
|
117
|
+
os_builds = [os_builds] if os_builds.class == String
|
118
|
+
build_plan_uris = []
|
119
|
+
action_handler.perform_action "Get OS Build Plan(s) info for #{machine_spec.name}" do
|
120
|
+
action_handler.report_progress "INFO: Getting OS Build Plan(s) for #{machine_spec.name}"
|
121
|
+
os_builds.each do |os_build|
|
122
|
+
uri = "/rest/index/resources?userQuery=\"'#{os_build}'\"&category=osdbuildplan"
|
123
|
+
while uri
|
124
|
+
matching_plans = rest_api(:icsp, :get, uri)
|
125
|
+
fail "Search failed for OSBP '#{os_build}'. Response: #{matching_plans}" unless matching_plans['members']
|
126
|
+
build_plan_uri = matching_plans['members'].find {|bp| bp['name'] == os_build}['uri'] rescue nil
|
127
|
+
break unless build_plan_uri.nil?
|
128
|
+
uri = URI.unescape(matching_plans['nextPageUri']) rescue nil
|
129
|
+
end
|
130
|
+
fail "OS build plan #{os_build} not found!" if build_plan_uri.nil?
|
131
|
+
build_plan_uris.push build_plan_uri
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Build options for the OS deployment
|
136
|
+
options = {}
|
137
|
+
options['X-API-Version'] = 104 if @current_icsp_api_version.between?(104, 108)
|
138
|
+
options['body'] = {
|
139
|
+
'osbpUris' => build_plan_uris,
|
140
|
+
'serverData' => [{
|
141
|
+
'serverUri' => my_server['uri']
|
142
|
+
}]
|
143
|
+
}
|
144
|
+
|
145
|
+
# Do the OS deployment
|
146
|
+
action_handler.perform_action "Run: #{os_builds} OS Build Plan(s) on #{machine_spec.name}" do
|
147
|
+
action_handler.report_progress "INFO: Running: #{os_builds} OS Build Plan(s) on #{machine_spec.name}"
|
148
|
+
task = rest_api(:icsp, :post, '/rest/os-deployment-jobs/?force=true', options)
|
149
|
+
task_uri = task['uri']
|
150
|
+
fail "Failed to start OS Deployment Job. Details: #{task['details'] || task['message'] || task}" unless task_uri
|
151
|
+
task = icsp_wait_for(task_uri, 720)
|
152
|
+
fail "Error running OS build plan(s) #{os_builds}: #{task['jobResult'].first['jobMessage']}\n#{task['jobResult'].first['jobResultErrorDetails']}" unless task == true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def icsp_configure_networking(action_handler, machine_spec, machine_options, my_server, profile)
|
157
|
+
action_handler.perform_action "Configure networking on #{machine_spec.name}" do
|
158
|
+
action_handler.report_progress "INFO: Configuring networking on #{machine_spec.name}"
|
159
|
+
personality_data = icsp_build_personality_data(machine_options, profile)
|
160
|
+
options = {}
|
161
|
+
options['X-API-Version'] = 104 if @current_icsp_api_version.between?(104, 108)
|
162
|
+
options['body'] = {
|
163
|
+
'serverData' => [{
|
164
|
+
'serverUri' => my_server['uri'],
|
165
|
+
'personalityData' => personality_data
|
166
|
+
}]
|
167
|
+
}
|
168
|
+
|
169
|
+
task = rest_api(:icsp, :post, '/rest/os-deployment-jobs/?force=true', options)
|
170
|
+
task_uri = task['uri']
|
171
|
+
fail "Failed to start network personalization job. Details: #{task['details']}" unless task_uri
|
172
|
+
task = icsp_wait_for(task_uri, 60) # Wait for up to 10 min
|
173
|
+
fail "Error running network personalization job: #{task['jobResult'].first['jobMessage']}\n#{task['jobResult'].first['jobResultErrorDetails']}" unless task == true
|
174
|
+
|
175
|
+
# Check if ICsp IP config matches machine options
|
176
|
+
requested_ips = []
|
177
|
+
machine_options[:driver_options][:connections].each do |_id, c|
|
178
|
+
requested_ips.push c[:ip4Address] if c[:ip4Address] && c[:dhcp] == false
|
179
|
+
end
|
180
|
+
my_server_connections = []
|
181
|
+
10.times do
|
182
|
+
my_server_connections = rest_api(:icsp, :get, my_server['uri'])['interfaces']
|
183
|
+
my_server_connections.each { |c| requested_ips.delete c['ipv4Addr'] }
|
184
|
+
break if requested_ips.empty?
|
185
|
+
print ','
|
186
|
+
sleep 10
|
187
|
+
end
|
188
|
+
puts "\nWARN: The following IPs are not visible on ICsp, so they may not have gotten configured correctly: #{requested_ips}" unless requested_ips.empty?
|
189
|
+
|
190
|
+
# Set interface data as normal node attributes
|
191
|
+
my_server_connections.each do |c|
|
192
|
+
c['oneViewId'] = profile['connections'].find {|x| x['mac'] == c['macAddr']}['id'] rescue nil
|
193
|
+
end
|
194
|
+
machine_spec.data['normal']['icsp'] ||= {}
|
195
|
+
machine_spec.data['normal']['icsp']['interfaces'] = my_server_connections
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Build options for the network configuration
|
200
|
+
def icsp_build_personality_data(machine_options, profile)
|
201
|
+
nics = []
|
202
|
+
if machine_options[:driver_options][:connections]
|
203
|
+
machine_options[:driver_options][:connections].each do |id, data|
|
204
|
+
c = Marshal.load(Marshal.dump(data))
|
205
|
+
next unless c[:dhcp] || c[:dhcpv4] || c[:ip4Address] || c[:ipv6autoconfig] || c[:staticNetworks] # Invalid network or only switch networks specified
|
206
|
+
begin
|
207
|
+
c[:macAddress] = profile['connections'].find {|x| x['id'] == id}['mac']
|
208
|
+
rescue NoMethodError
|
209
|
+
ids = []
|
210
|
+
profile['connections'].each {|x| ids.push x['id']}
|
211
|
+
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."
|
212
|
+
end
|
213
|
+
if @current_icsp_api_version.between?(104, 108)
|
214
|
+
icsp_v104_parse_connection(machine_options, c)
|
215
|
+
else
|
216
|
+
c[:mask] ||= machine_options[:driver_options][:mask]
|
217
|
+
c[:dhcp] ||= false
|
218
|
+
c[:gateway] ||= machine_options[:driver_options][:gateway]
|
219
|
+
c[:dns] ||= machine_options[:driver_options][:dns]
|
220
|
+
end
|
221
|
+
nics.push c
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
if @current_icsp_api_version.between?(104, 108)
|
226
|
+
personality_data = icsp_v104_build_personality_data(machine_options, nics)
|
227
|
+
else
|
228
|
+
personality_data = {
|
229
|
+
'hostName' => machine_options[:driver_options][:host_name],
|
230
|
+
'domainType' => machine_options[:driver_options][:domainType],
|
231
|
+
'domainName' => machine_options[:driver_options][:domainName],
|
232
|
+
'nics' => nics
|
233
|
+
}
|
234
|
+
end
|
235
|
+
personality_data
|
236
|
+
end
|
237
|
+
|
238
|
+
def destroy_icsp_server(action_handler, machine_spec)
|
239
|
+
my_server = get_icsp_server_by_sn(machine_spec.reference['serial_number'])
|
240
|
+
return false if my_server.nil? || my_server['uri'].nil?
|
241
|
+
|
242
|
+
action_handler.perform_action "Delete server #{machine_spec.name} from ICSP" do
|
243
|
+
task = rest_api(:icsp, :delete, my_server['uri']) # TODO: This returns nil instead of task info
|
244
|
+
if task['uri']
|
245
|
+
task_uri = task['uri']
|
246
|
+
90.times do # Wait for up to 15 minutes
|
247
|
+
task = rest_api(:icsp, :get, task_uri)
|
248
|
+
break if task['taskState'].downcase == 'completed'
|
249
|
+
print '.'
|
250
|
+
sleep 10
|
251
|
+
end
|
252
|
+
fail "Deleting os deployment server #{machine_spec.name} at icsp failed!" unless task['taskState'].downcase == 'completed'
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def icsp_configure_nic_teams(machine_options, profile)
|
258
|
+
return false if machine_options[:driver_options][:connections].nil?
|
259
|
+
teams = {}
|
260
|
+
|
261
|
+
machine_options[:driver_options][:connections].each do |id, options|
|
262
|
+
next unless options.is_a?(Hash) && options[:team]
|
263
|
+
fail "#{options[:team]}: Team names must not include hyphens" if options[:team].to_s.match('-')
|
264
|
+
teams[options[:team].to_s] ||= []
|
265
|
+
begin
|
266
|
+
mac = profile['connections'].find {|x| x['id'] == id}['mac']
|
267
|
+
teams[options[:team].to_s].push mac
|
268
|
+
rescue NoMethodError
|
269
|
+
ids = []
|
270
|
+
profile['connections'].each {|x| ids.push x['id']}
|
271
|
+
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."
|
272
|
+
end
|
273
|
+
end
|
274
|
+
team_strings = []
|
275
|
+
teams.each do |name, macs|
|
276
|
+
fail "Team '#{name}' must have at least 2 associated connections to form a NIC team" unless macs.size >= 2
|
277
|
+
team_strings.push "#{name}-#{macs.join(',')}"
|
278
|
+
end
|
279
|
+
machine_options[:driver_options][:custom_attributes] ||= {}
|
280
|
+
machine_options[:driver_options][:custom_attributes][:teams] = team_strings.join('|')
|
281
|
+
end
|
282
|
+
end # End module
|