Class: Kitchen::Driver::Vra

Inherits:
Base
  • Object
show all
Defined in:
lib/kitchen/driver/vra.rb

Overview

rubocop:disable Metrics/ClassLength

Instance Method Summary collapse

Instance Method Details

#c_loadObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/kitchen/driver/vra.rb', line 99

def c_load
  if File.exist? '.kitchen/cached_vra'
    encrypted = File.read('.kitchen/cached_vra')
    iv_user = Base64.decode64(encrypted.split(':')[0] + '\n')
    username = Base64.decode64(encrypted.split(':')[1] + "\n")
    iv_pwd = Base64.decode64(encrypted.split(':')[2] + "\n")
    password = Base64.decode64(encrypted.split(':')[3] + "\n")
    cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
    cipher.decrypt
    cipher.key = Digest::SHA1.hexdigest(config[:base_url])
    cipher.iv = iv_user
    config[:username] = cipher.update(username) + cipher.final
    cipher.iv = iv_pwd
    config[:password] = cipher.update(password) + cipher.final
  end
rescue
  puts 'Failed to load cached credentials'
end

#c_saveObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/kitchen/driver/vra.rb', line 81

def c_save
  cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
  cipher.encrypt
  cipher.key = Digest::SHA1.hexdigest(config[:base_url])
  iv_user = cipher.random_iv
  cipher.iv = iv_user
  username = cipher.update(config[:username]) + cipher.final
  iv_pwd = cipher.random_iv
  cipher.iv = iv_pwd
  password = cipher.update(config[:password]) + cipher.final
  output = "#{Base64.encode64(iv_user).strip!}:#{Base64.encode64(username).strip!}:#{Base64.encode64(iv_pwd).strip!}:#{Base64.encode64(password).strip!}"
  file = File.open('.kitchen/cached_vra', 'w')
  file.write(output)
  file.close
rescue
  puts 'Unable to save credentials'
end

#catalog_requestObject

rubocop:disable Metrics/MethodLength



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/kitchen/driver/vra.rb', line 210

def catalog_request # rubocop:disable Metrics/MethodLength
  unless config[:catalog_name].nil?
    info('Fetching Catalog ID by Catalog Name')
    response =  vra_client.catalog.fetch_catalog_items(config[:catalog_name])
    parsed_json = JSON.parse(response.body)
    begin
      config[:catalog_id] = parsed_json['content'][0]['catalogItemId']
    rescue
      puts "Unable to retrieve Catalog ID from Catalog Name: #{config[:catalog_name]}"
    end
  end

  catalog_request = vra_client.catalog.request(config[:catalog_id])

  catalog_request.cpus          = config[:cpus]
  catalog_request.memory        = config[:memory]
  catalog_request.shirt_size    = config[:shirt_size]	unless config[:shirt_size].nil?
  catalog_request.requested_for = config[:requested_for]
  catalog_request.lease_days    = config[:lease_days]    unless config[:lease_days].nil?
  catalog_request.notes         = config[:notes]         unless config[:notes].nil?

  unless config[:subtenant_name].nil?
    info('Fetching Subtenant ID by Subtenant Name')
    response = vra_client.fetch_subtenant_items(config[:tenant], config[:subtenant_name])
    parsed_json = JSON.parse(response.body)
    begin
      config[:subtenant_id] = parsed_json['content'][0]['id']
    rescue
      puts "Unable to retrieve Subtenant ID from Subtenant Name: #{config[:subtenant_name]}"
    end
  end
  catalog_request.subtenant_id = config[:subtenant_id] unless config[:subtenant_id].nil?

  config[:extra_parameters].each do |key, value_data|
    catalog_request.set_parameters(key, value_data)
  end

  catalog_request
end

#check_config(force_change = false) ⇒ Object



71
72
73
74
75
76
77
78
79
# File 'lib/kitchen/driver/vra.rb', line 71

def check_config(force_change = false)
  config[:username] = config[:username] || ENV['VRA_USER_NAME']
  config[:password] = config[:password] || ENV['VRA_USER_PASSWORD']
  c_load if config[:username].nil? && config[:password].nil?

  config[:username] = ask('Enter Username: e.g. username@domain') if config[:username].nil? || force_change
  config[:password] = ask('Enter password: ') { |q| q.echo = '*' } if config[:password].nil? || force_change
  c_save if config[:cache_credentials]
end

#create(state) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/kitchen/driver/vra.rb', line 118

def create(state)
  return if state[:resource_id]

  server = request_server
  state[:resource_id] = server.id
  state[:hostname]    = hostname_for(server)
  state[:ssh_key]     = config[:private_key_path] unless config[:private_key_path].nil?

  wait_for_server(state, server)
  info("Server #{server.id} (#{server.name}) ready.")
end

#destroy(state) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/kitchen/driver/vra.rb', line 186

def destroy(state)
  return if state[:resource_id].nil?

  begin
    server = vra_client.resources.by_id(state[:resource_id])
  rescue ::Vra::Exception::NotFound
    warn("No server found with ID #{state[:resource_id]}, assuming it has been destroyed already.")
    return
  end

  begin
    destroy_request = server.destroy
  rescue ::Vra::Exception::NotFound
    info('Server not found, or no destroy action available, perhaps because it is already destroyed.')
    return
  end
  info("Destroy request #{destroy_request.id} submitted.")
  wait_for_request(destroy_request)
  info('Destroy request complete.')

  File.delete('.kitchen/cached_vra') if File.exist?('.kitchen/cached_vra')
  info('Removed cached file')
end

#hostname_for(server) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/kitchen/driver/vra.rb', line 130

def hostname_for(server)
  if config[:use_dns]
    raise 'No server name returned for the vRA request' if server.name.nil?
    return config[:dns_suffix] ? "#{server.name}.#{config[:dns_suffix]}" : server.name
  end

  ip_address = server.ip_addresses.first
  if ip_address.nil?
    warn("Server #{server.id} has no IP address. Falling back to server name (#{server.name})...")
    server.name
  else
    ip_address
  end
end

#nameObject



67
68
69
# File 'lib/kitchen/driver/vra.rb', line 67

def name
  'vRA'
end

#request_serverObject



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/kitchen/driver/vra.rb', line 145

def request_server
  info('Building vRA catalog request...')
   = catalog_request.submit
  info("Catalog request #{.id} submitted.")

  wait_for_request()
  raise "The vRA request failed: #{.completion_details}" if .failed?

  servers = .resources.select(&:vm?)
  raise 'The vRA request created more than one server. The catalog blueprint should only return one.' if servers.size > 1
  raise 'the vRA request did not create any servers.' if servers.size.zero?

  servers.first
end

#vra_clientObject



250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/kitchen/driver/vra.rb', line 250

def vra_client
  check_config config[:cache_credentials]
  @client ||= ::Vra::Client.new(
    base_url:   config[:base_url],
    username:   config[:username],
    password:   config[:password],
    tenant:     config[:tenant],
    verify_ssl: config[:verify_ssl]
  )
rescue => _e
  check_config true
end

#wait_for_request(request) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/kitchen/driver/vra.rb', line 263

def wait_for_request(request)
  # config = check_config config

  last_status = ''
  wait_time   = config[:request_timeout]
  sleep_time  = config[:request_refresh_rate]
  Timeout.timeout(wait_time) do
    loop do
      request.refresh
      break if request.completed?

      unless last_status == request.status
        last_status = request.status
        info("Current request status: #{request.status}")
      end

      sleep sleep_time
    end
  end
rescue Timeout::Error
  error("Request did not complete in #{wait_time} seconds. Check the Requests tab in the vRA UI for more information.")
  raise
end

#wait_for_server(state, server) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/kitchen/driver/vra.rb', line 160

def wait_for_server(state, server)
  info("Server #{server.id} (#{server.name}) created. Waiting until ready...")

  try = 0
  sleep_time = 0

  begin
    instance.transport.connection(state).wait_until_ready
  rescue => e
    warn("Server #{server.id} (#{server.name}) not reachable: #{e.class} -- #{e.message}")

    try += 1
    sleep_time += 5 if sleep_time < 30

    if try > config[:server_ready_retries]
      error('Retries exceeded. Destroying server...')
      destroy(state)
      raise
    else
      warn("Sleeping #{sleep_time} seconds and retrying...")
      sleep sleep_time
      retry
    end
  end
end