vmware-vra 2.7.0 → 3.0.1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/CHANGELOG.md +27 -1
  4. data/README.md +91 -141
  5. data/Rakefile +1 -12
  6. data/lib/vra/catalog.rb +39 -8
  7. data/lib/vra/catalog_base.rb +62 -0
  8. data/lib/vra/catalog_item.rb +29 -75
  9. data/lib/vra/catalog_source.rb +116 -0
  10. data/lib/vra/catalog_type.rb +56 -0
  11. data/lib/vra/client.rb +72 -53
  12. data/lib/vra/deployment.rb +155 -0
  13. data/lib/vra/deployment_request.rb +117 -0
  14. data/lib/vra/{resources.rb → deployments.rb} +26 -17
  15. data/lib/vra/exceptions.rb +2 -2
  16. data/lib/vra/http.rb +20 -7
  17. data/lib/vra/request.rb +28 -36
  18. data/lib/vra/request_parameters.rb +12 -10
  19. data/lib/vra/resource.rb +33 -203
  20. data/lib/vra/version.rb +2 -2
  21. data/lib/vra.rb +15 -12
  22. data/spec/catalog_item_spec.rb +64 -222
  23. data/spec/catalog_source_spec.rb +178 -0
  24. data/spec/catalog_spec.rb +112 -72
  25. data/spec/catalog_type_spec.rb +114 -0
  26. data/spec/client_spec.rb +287 -228
  27. data/spec/deployment_request_spec.rb +192 -0
  28. data/spec/deployment_spec.rb +227 -0
  29. data/spec/deployments_spec.rb +80 -0
  30. data/spec/fixtures/resource/sample_catalog_item.json +18 -0
  31. data/spec/fixtures/resource/sample_catalog_item_2.json +18 -0
  32. data/spec/fixtures/resource/sample_catalog_source.json +20 -0
  33. data/spec/fixtures/resource/sample_catalog_type.json +49 -0
  34. data/spec/fixtures/resource/sample_dep_actions.json +58 -0
  35. data/spec/fixtures/resource/sample_dep_request.json +19 -0
  36. data/spec/fixtures/resource/sample_dep_resource.json +112 -0
  37. data/spec/fixtures/resource/sample_deployment.json +26 -0
  38. data/spec/fixtures/resource/sample_entitlements.json +25 -0
  39. data/spec/http_spec.rb +63 -61
  40. data/spec/request_spec.rb +62 -68
  41. data/spec/resource_spec.rb +71 -390
  42. data/spec/spec_helper.rb +10 -4
  43. data/vmware-vra.gemspec +2 -4
  44. metadata +42 -32
  45. data/.travis.yml +0 -14
  46. data/lib/vra/catalog_request.rb +0 -127
  47. data/lib/vra/requests.rb +0 -41
  48. data/spec/catalog_request_spec.rb +0 -265
  49. data/spec/requests_spec.rb +0 -60
  50. data/spec/resources_spec.rb +0 -71
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,114 +17,68 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require "ffi_yajl"
20
+ require "ffi_yajl" unless defined?(FFI_Yajl)
21
21
  require "vra/catalog"
22
22
 
23
23
  module Vra
24
- class CatalogItem
25
- attr_reader :id, :client
26
- def initialize(client, opts)
27
- @client = client
28
- @id = opts[:id]
29
- @catalog_item_data = opts[:data]
24
+ # Class that represents the Catalog Item
25
+ class CatalogItem < Vra::CatalogBase
26
+ INDEX_URL = '/catalog/api/admin/items'
30
27
 
31
- if @id.nil? && @catalog_item_data.nil?
32
- raise ArgumentError, "must supply an id or a catalog item data hash"
33
- end
28
+ attr_reader :project_id
34
29
 
35
- if !@id.nil? && !@catalog_item_data.nil?
36
- raise ArgumentError, "must supply an id OR a catalog item data hash, not both"
37
- end
30
+ def initialize(client, opts = {})
31
+ super
32
+ @project_id = opts[:project_id]
33
+ validate!
38
34
 
39
- if @catalog_item_data.nil?
35
+ if @data.nil?
40
36
  fetch_catalog_item
41
37
  else
42
- @id = @catalog_item_data["id"]
38
+ @id = @data['id']
43
39
  end
44
40
  end
45
41
 
46
42
  def fetch_catalog_item
47
- @catalog_item_data = client.get_parsed("/catalog-service/api/consumer/catalogItems/#{id}")
43
+ @data = client.get_parsed("/catalog/api/admin/items/#{id}")
48
44
  rescue Vra::Exception::HTTPNotFound
49
45
  raise Vra::Exception::NotFound, "catalog ID #{id} does not exist"
50
46
  end
51
47
 
52
48
  def name
53
- @catalog_item_data["name"]
49
+ data['name']
54
50
  end
55
51
 
56
52
  def description
57
- @catalog_item_data["description"]
58
- end
59
-
60
- def status
61
- @catalog_item_data["status"]
53
+ data['description']
62
54
  end
63
55
 
64
- def organization
65
- return {} if @catalog_item_data["organization"].nil?
66
-
67
- @catalog_item_data["organization"]
56
+ def source_id
57
+ data['sourceId']
68
58
  end
69
59
 
70
- def tenant_id
71
- organization["tenantRef"]
60
+ def source_name
61
+ data['sourceName']
72
62
  end
73
63
 
74
- def tenant_name
75
- organization["tenantLabel"]
64
+ def source
65
+ @source ||= Vra::CatalogSource.new(client, id: source_id)
76
66
  end
77
67
 
78
- def subtenant_id
79
- organization["subtenantRef"]
68
+ def type
69
+ @type ||= Vra::CatalogType.new(client, data: data['type'])
80
70
  end
81
71
 
82
- def subtenant_name
83
- organization["subtenantLabel"]
72
+ def icon_id
73
+ data['iconId']
84
74
  end
85
75
 
86
- def blueprint_id
87
- @catalog_item_data["providerBinding"]["bindingId"]
76
+ def entitle!(opts = {})
77
+ super(opts.merge(type: 'CatalogItemIdentifier'))
88
78
  end
89
79
 
90
- # @param [String] - the id of the catalog item
91
- # @param [Vra::Client] - a vra client object
92
- # @return [String] - returns a json string of the catalog template
93
- def self.dump_template(client, id)
94
- response = client.http_get("/catalog-service/api/consumer/entitledCatalogItems/#{id}/requests/template")
95
- response.body
96
- end
97
-
98
- # @param client [Vra::Client] - a vra client object
99
- # @param id [String] - the id of the catalog item
100
- # @param filename [String] - the name of the file you want to output the template to
101
- # if left blank, will default to the id of the item
102
- # @note outputs the catalog template to a file in serialized format
103
- def self.write_template(client, id, filename = nil)
104
- filename ||= "#{id}.json"
105
- begin
106
- contents = dump_template(client, id)
107
- data = JSON.parse(contents)
108
- pretty_contents = JSON.pretty_generate(data)
109
- File.write(filename, pretty_contents)
110
- return filename
111
- rescue Vra::Exception::HTTPError => e
112
- raise e
113
- end
114
- end
115
-
116
- # @param [Vra::Client] - a vra client object
117
- # @param [String] - the directory path to write the files to
118
- # @param [Boolean] - set to true if you wish the file name to be the id of the catalog item
119
- # @return [Array[String]] - a array of all the files that were generated
120
- def self.dump_templates(client, dir_name = "vra_templates", use_id = false)
121
- FileUtils.mkdir_p(dir_name) unless File.exist?(dir_name)
122
- client.catalog.entitled_items.map do |c|
123
- id = use_id ? c.id : c.name.tr(" ", "_")
124
- filename = File.join(dir_name, "#{id}.json").downcase
125
- write_template(client, c.id, filename)
126
- filename
127
- end
80
+ def self.entitle!(client, id)
81
+ new(client, id: id).entitle!
128
82
  end
129
83
  end
130
84
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ require 'ffi_yajl' unless defined?(FFI_Yajl)
20
+
21
+ module Vra
22
+ # Class that represents the Catalog Source
23
+ class CatalogSource < Vra::CatalogBase
24
+ INDEX_URL = '/catalog/api/admin/sources'
25
+
26
+ # @param client [Vra::Client] - a vra client object
27
+ # @param opts [Hash] - Contains the either id of the catalog or the data hash
28
+ # Either one of id or data hash is required, must not supply both
29
+ def initialize(client, opts)
30
+ super
31
+ validate!
32
+ fetch_data
33
+ end
34
+
35
+ def name
36
+ data['name']
37
+ end
38
+
39
+ def catalog_type_id
40
+ data['typeId']
41
+ end
42
+
43
+ def catalog_type
44
+ @catalog_type ||= Vra::CatalogType.new(client, id: catalog_type_id)
45
+ end
46
+
47
+ def config
48
+ data['config']
49
+ end
50
+
51
+ def global?
52
+ data['global'] == true
53
+ end
54
+
55
+ def project_id
56
+ config['sourceProjectId']
57
+ end
58
+
59
+ def entitle!(opts = {})
60
+ super(opts.merge(type: 'CatalogSourceIdentifier'))
61
+ end
62
+
63
+ class << self
64
+ # Method to create a catalog source
65
+ def create(client, opts)
66
+ validate_create!(opts)
67
+
68
+ response = client.http_post(
69
+ '/catalog/api/admin/sources',
70
+ FFI_Yajl::Encoder.encode(create_params(opts)),
71
+ opts[:skip_auth] || false
72
+ )
73
+
74
+ return false unless response.success?
75
+
76
+ new(client, data: FFI_Yajl::Parser.parse(response.body))
77
+ end
78
+
79
+ def entitle!(client, id)
80
+ new(client, id: id).entitle!
81
+ end
82
+
83
+ private
84
+
85
+ def validate_create!(opts)
86
+ %i[name catalog_type_id project_id].each do |arg|
87
+ raise ArgumentError, "#{arg} param is required to perform the create action" unless opts.key?(arg)
88
+ end
89
+ end
90
+
91
+ def create_params(opts)
92
+ {
93
+ 'name': opts[:name],
94
+ 'typeId': opts[:catalog_type_id],
95
+ 'config': {
96
+ 'sourceProjectId': opts[:project_id]
97
+ }
98
+ }
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def fetch_data
105
+ fetch_catalog_data && return if data.nil?
106
+
107
+ @id = data['id']
108
+ end
109
+
110
+ def fetch_catalog_data
111
+ @data = client.get_parsed("/catalog/api/admin/sources/#{id}")
112
+ rescue Vra::Exception::HTTPNotFound
113
+ raise Vra::Exception::NotFound, "catalog source ID #{id} does not exist"
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ module Vra
20
+ # Class that represents the Catalog Type
21
+ class CatalogType < Vra::CatalogBase
22
+ INDEX_URL = '/catalog/api/types'
23
+
24
+ def initialize(client, opts = {})
25
+ super
26
+ validate!
27
+ fetch_data
28
+ end
29
+
30
+ def name
31
+ data['name']
32
+ end
33
+
34
+ def base_url
35
+ data['baseUri']
36
+ end
37
+
38
+ def config_schema
39
+ data['configSchema']
40
+ end
41
+
42
+ def icon_id
43
+ data['iconId']
44
+ end
45
+
46
+ private
47
+
48
+ def fetch_data
49
+ @id = data['id'] and return unless data.nil?
50
+
51
+ @data = client.get_parsed("/catalog/api/types/#{id}")
52
+ rescue Vra::Exception::HTTPNotFound
53
+ raise Vra::Exception::NotFound, "catalog type ID #{id} does not exist"
54
+ end
55
+ end
56
+ end
data/lib/vra/client.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,22 +17,27 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require "ffi_yajl"
21
- require "passwordmasker"
22
- require "vra/http"
20
+ require 'ffi_yajl' unless defined?(FFI_Yajl)
21
+ require 'passwordmasker'
22
+ require 'vra/http'
23
23
 
24
24
  module Vra
25
25
  class Client
26
+ REFRESH_TOKEN_URL = '/csp/gateway/am/api/login?access_token'
27
+ ACCESS_TOKEN_URL = '/iaas/api/login'
28
+ ROLES_URL = '/csp/gateway/am/api/loggedin/user/orgs'
29
+
26
30
  attr_accessor :page_size
27
31
 
28
32
  def initialize(opts)
29
- @base_url = opts[:base_url]
30
- @username = opts[:username]
31
- @password = PasswordMasker.new(opts[:password])
32
- @tenant = opts[:tenant]
33
- @verify_ssl = opts.fetch(:verify_ssl, true)
34
- @bearer_token = PasswordMasker.new(nil)
35
- @page_size = opts.fetch(:page_size, 20)
33
+ @base_url = opts[:base_url]
34
+ @username = opts[:username]
35
+ @password = PasswordMasker.new(opts[:password])
36
+ @tenant = opts[:tenant]
37
+ @verify_ssl = opts.fetch(:verify_ssl, true)
38
+ @refresh_token = PasswordMasker.new(nil)
39
+ @access_token = PasswordMasker.new(nil)
40
+ @page_size = opts.fetch(:page_size, 20)
36
41
 
37
42
  validate_client_options!
38
43
  end
@@ -43,15 +48,11 @@ module Vra
43
48
  #
44
49
 
45
50
  def catalog
46
- Vra::Catalog.new(self)
47
- end
48
-
49
- def requests(*args)
50
- Vra::Requests.new(self, *args)
51
+ @catalog ||= Vra::Catalog.new(self)
51
52
  end
52
53
 
53
- def resources(*args)
54
- Vra::Resources.new(self, *args)
54
+ def deployments
55
+ @deployments ||= Vra::Deployments.new(self)
55
56
  end
56
57
 
57
58
  #########################
@@ -59,55 +60,73 @@ module Vra
59
60
  # client methods
60
61
  #
61
62
 
62
- def bearer_token
63
- @bearer_token.value
63
+ def access_token
64
+ @access_token.value
65
+ end
66
+
67
+ def refresh_token
68
+ @refresh_token.value
69
+ end
70
+
71
+ def access_token=(value)
72
+ @access_token.value = value
64
73
  end
65
74
 
66
- def bearer_token=(value)
67
- @bearer_token.value = value
75
+ def refresh_token=(value)
76
+ @refresh_token.value = value
68
77
  end
69
78
 
70
- def bearer_token_request_body
79
+ def token_params
71
80
  {
72
- "username" => @username,
73
- "password" => @password.value,
74
- "tenant" => @tenant,
81
+ 'username': @username,
82
+ 'password': @password.value,
83
+ 'tenant': @tenant
75
84
  }
76
85
  end
77
86
 
78
87
  def request_headers
79
- headers = {}
80
- headers["Accept"] = "application/json"
81
- headers["Content-Type"] = "application/json"
82
- headers["Authorization"] = "Bearer #{@bearer_token.value}" unless @bearer_token.value.nil?
88
+ headers = {}
89
+ headers['Accept'] = 'application/json'
90
+ headers['Content-Type'] = 'application/json'
91
+ headers['csp-auth-token'] = @access_token.value unless @access_token.value.nil?
92
+
83
93
  headers
84
94
  end
85
95
 
86
96
  def authorize!
87
- generate_bearer_token unless authorized?
97
+ generate_access_token unless authorized?
88
98
 
89
- raise Vra::Exception::Unauthorized, "Unable to authorize against vRA" unless authorized?
99
+ raise Vra::Exception::Unauthorized, 'Unable to authorize against vRA' unless authorized?
90
100
  end
91
101
 
92
102
  def authorized?
93
- return false if @bearer_token.value.nil?
103
+ return false if @access_token.value.nil?
94
104
 
95
- response = http_head("/identity/api/tokens/#{@bearer_token.value}", :skip_auth)
96
- response.success_no_content?
105
+ response = http_head(ROLES_URL, :skip_auth)
106
+ response.success?
97
107
  end
98
108
 
99
- def generate_bearer_token
100
- @bearer_token.value = nil
109
+ def generate_access_token
110
+ @refresh_token.value = nil
111
+ @access_token.value = nil
101
112
  validate_client_options!
102
113
 
103
- response = http_post("/identity/api/tokens",
104
- FFI_Yajl::Encoder.encode(bearer_token_request_body),
114
+ # VRA 8 has a two-step authentication process - This probably breaks VRA7, who knows?!?
115
+ # First step: Sending Username/Password to get a Refresh Token
116
+ refresh_response = http_post(REFRESH_TOKEN_URL,
117
+ FFI_Yajl::Encoder.encode(token_params),
105
118
  :skip_auth)
106
- unless response.success_ok?
107
- raise Vra::Exception::Unauthorized, "Unable to get bearer token: #{response.body}"
108
- end
119
+ raise Vra::Exception::Unauthorized, "Unable to get the refresh token: #{refresh_response.body}" unless refresh_response.success_ok?
120
+
121
+ refresh_response_body = FFI_Yajl::Parser.parse(refresh_response.body)
122
+ @refresh_token.value = refresh_response_body['refresh_token']
109
123
 
110
- @bearer_token.value = FFI_Yajl::Parser.parse(response.body)["id"]
124
+ # Second Step: Sending the refresh token to a separate endpoint to get an Access Token
125
+ access_response = http_post(ACCESS_TOKEN_URL, "{ \"refreshToken\": \"#{@refresh_token.value}\" }", :skip_auth)
126
+ raise Vra::Exception::Unauthorized, "Unable to get the access token: #{access_response.body}" unless access_response.success_ok?
127
+
128
+ access_response_body = FFI_Yajl::Parser.parse(access_response.body)
129
+ @access_token.value = access_response_body['token']
111
130
  end
112
131
 
113
132
  def full_url(path)
@@ -140,21 +159,26 @@ module Vra
140
159
  response.body
141
160
  end
142
161
 
162
+ def http_delete(path, skip_auth = nil)
163
+ http_fetch(:delete, path, skip_auth)
164
+ end
165
+
143
166
  def get_parsed(path)
144
167
  FFI_Yajl::Parser.parse(http_get!(path))
145
168
  end
146
169
 
147
- def http_get_paginated_array!(path)
170
+ def http_get_paginated_array!(path, filter = nil)
148
171
  items = []
149
- page = 1
150
- base_path = path + "?limit=#{page_size}"
172
+ page = 0
173
+ base_path = path + "?$top=#{page_size}"
174
+ base_path += "&#{filter}" if filter
151
175
 
152
176
  loop do
153
- response = get_parsed("#{base_path}&page=#{page}")
177
+ response = get_parsed("#{base_path}&$skip=#{page * page_size}")
154
178
  items += response["content"]
155
179
 
156
- break if page >= response["metadata"]["totalPages"]
157
180
  page += 1
181
+ break if page >= response["totalPages"]
158
182
  end
159
183
 
160
184
  if items.uniq!
@@ -218,10 +242,5 @@ module Vra
218
242
  rescue URI::InvalidURIError
219
243
  false
220
244
  end
221
-
222
- def fetch_subtenant_items(tenant, subtenant_name)
223
- http_get("/identity/api/tenants/#{tenant}/subtenants?%24filter=name+eq+'#{subtenant_name}'")
224
- end
225
-
226
245
  end
227
246
  end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require "ffi_yajl" unless defined?(FFI_Yajl)
21
+
22
+ module Vra
23
+ # Class that represents the Deployment Object
24
+ class Deployment
25
+ INDEX_URL = '/deployment/api/deployments'
26
+
27
+ attr_reader :id
28
+
29
+ def initialize(client, opts = {})
30
+ @client = client
31
+ @id = opts[:id]
32
+ @data = opts[:data]
33
+ validate!
34
+
35
+ if @data.nil?
36
+ refresh
37
+ elsif @id.nil?
38
+ @id = @data['id']
39
+ end
40
+ end
41
+
42
+ def name
43
+ @data['name']
44
+ end
45
+
46
+ def description
47
+ @data['description']
48
+ end
49
+
50
+ def org_id
51
+ @data['orgId']
52
+ end
53
+
54
+ def blueprint_id
55
+ @data['blueprintId']
56
+ end
57
+
58
+ def owner
59
+ @data['ownedBy']
60
+ end
61
+
62
+ def status
63
+ @data['status']
64
+ end
65
+
66
+ def successful?
67
+ status == 'CREATE_SUCCESSFUL'
68
+ end
69
+
70
+ def failed?
71
+ status == 'CREATE_FAILED'
72
+ end
73
+
74
+ def completed?
75
+ successful? || failed?
76
+ end
77
+
78
+ def actions
79
+ @actions = client.get_parsed("/deployment/api/deployments/#{id}/actions")
80
+ end
81
+
82
+ def action_id_by_name(action_name)
83
+ action = actions.find { |x| x['name'] == action_name}
84
+ return if action.nil?
85
+
86
+ action['id']
87
+ end
88
+
89
+ def resources
90
+ response = client.get_parsed("/deployment/api/deployments/#{id}/resources")
91
+
92
+ response['content'].map! { |x| Vra::Resource.new(client, id, data: x) }
93
+ end
94
+
95
+ def resource_by_id(res_id)
96
+ Vra::Resource.new(client, id, id: res_id)
97
+ end
98
+
99
+ def requests
100
+ response = client.get_parsed("/deployment/api/deployments/#{id}/requests")
101
+
102
+ response['content'].map! { |x| Vra::Request.new(client, id, id: x['id'], data: x) }
103
+ end
104
+
105
+ def refresh
106
+ @data = client.get_parsed("/deployment/api/deployments/#{id}")
107
+ rescue Vra::Exception::HTTPNotFound
108
+ raise Vra::Exception::NotFound, "deployment with ID #{id} does not exist"
109
+ end
110
+
111
+ def destroy(reason = '')
112
+ action_id = action_id_by_name('Delete')
113
+ raise Vra::Exception::NotFound, "No destroy action found for resource #{@id}" if action_id.nil?
114
+
115
+ submit_action_request(action_id, reason)
116
+ end
117
+
118
+ def power_off(reason = '')
119
+ action_id = action_id_by_name('PowerOff')
120
+ raise Vra::Exception::NotFound, "No power-off action found for resource #{@id}" if action_id.nil?
121
+
122
+ submit_action_request(action_id, reason)
123
+ end
124
+
125
+ def power_on(reason = nil)
126
+ action_id = action_id_by_name('PowerOn')
127
+ raise Vra::Exception::NotFound, "No power-on action found for resource #{@id}" if action_id.nil?
128
+
129
+ submit_action_request(action_id, reason)
130
+ end
131
+
132
+ private
133
+
134
+ attr_reader :client, :data
135
+
136
+ def validate!
137
+ raise ArgumentError, 'must supply id or data hash' if @id.nil? && @data.nil?
138
+ end
139
+
140
+ def submit_action_request(action_id, reason)
141
+ response = client.http_post!("/deployment/api/deployments/#{id}/requests",
142
+ FFI_Yajl::Encoder.encode(submit_action_payload(action_id, reason)))
143
+
144
+ Vra::Request.new(client, id, data: FFI_Yajl::Parser.parse(response))
145
+ end
146
+
147
+ def submit_action_payload(action_id, reason)
148
+ {
149
+ "actionId": action_id,
150
+ "inputs": {},
151
+ "reason": reason
152
+ }
153
+ end
154
+ end
155
+ end