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



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/kitchen/driver/vra.rb', line 103

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.new("cip-her-aes")
    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



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/kitchen/driver/vra.rb', line 85

def c_save
  cipher = OpenSSL::Cipher.new("cip-her-aes")
  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



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
249
250
251
252
# File 'lib/kitchen/driver/vra.rb', line 220

def catalog_request # rubocop:disable Metrics/MethodLength
  unless config[:catalog_name].nil?
    info("Fetching Catalog ID by Catalog Name")
    catalog_items = vra_client.catalog.fetch_catalog_items(config[:catalog_name])
    begin
      config[:catalog_id] = catalog_items[0].id
      info("Using Catalog with ID: #{catalog_items[0].id}")
    rescue
      error("Unable to retrieve Catalog ID from Catalog Name: #{config[:catalog_name]}")
    end
  end

  if config[:catalog_id].nil?
    raise Kitchen::InstanceFailure, "Unable to create deployment without a valid catalog"
  end

  deployment_params = {
    image_mapping: config[:image_mapping],
    flavor_mapping: config[:flavor_mapping],
    project_id: config[:project_id],
    version: config[:version],
  }.tap do |h|
    h[:name] = config[:deployment_name] unless config[:unique_name]
  end

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

  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



75
76
77
78
79
80
81
82
83
# File 'lib/kitchen/driver/vra.rb', line 75

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. johnsmith") 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



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/kitchen/driver/vra.rb', line 122

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

  server = request_server
  state[:deployment_id] = server.deployment_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.deployment_id} (#{server.name}) ready.")
end

#destroy(state) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/kitchen/driver/vra.rb', line 196

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

  begin
    server = vra_client.deployments.by_id(state[:deployment_id])
  rescue ::Vra::Exception::NotFound
    warn("No server found with ID #{state[:deployment_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



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/kitchen/driver/vra.rb', line 134

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_address
  if ip_address.nil?
    warn("Server #{server.deployment_id} has no IP address. Falling back to server name (#{server.name})...")
    server.name
  else
    ip_address
  end
end

#nameObject



71
72
73
# File 'lib/kitchen/driver/vra.rb', line 71

def name
  "vRA"
end

#request_serverObject



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/kitchen/driver/vra.rb', line 150

def request_server
  info("Building vRA catalog request...")

  deployment_request = catalog_request.submit

  info("Catalog request #{deployment_request.id} submitted.")
  if config[:unique_name]
    info("Deployment name is deployment_#{deployment_request.id}")
  end

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

  servers = deployment_request.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 == 0

  servers.first
end

#vra_clientObject



254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/kitchen/driver/vra.rb', line 254

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

#wait_for_request(request) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/kitchen/driver/vra.rb', line 267

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



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/kitchen/driver/vra.rb', line 170

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