knife-vcenter 1.0.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.
@@ -0,0 +1,269 @@
1
+ # Copyright 2014-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'savon'
5
+ require 'nokogiri'
6
+ require 'date'
7
+ require 'securerandom'
8
+
9
+ # A little utility library for VMware SSO.
10
+ # For now, this is not a general purpose library that covers all
11
+ # the interfaces of the SSO service.
12
+ # Specifically, the support is limited to the following:
13
+ # * request bearer token.
14
+ module SSO
15
+
16
+ # The XML date format.
17
+ DATE_FORMAT = "%FT%T.%LZ"
18
+
19
+ # The XML namespaces that are required: SOAP, WSDL, et al.
20
+ NAMESPACES = {
21
+ "xmlns:S" => "http://schemas.xmlsoap.org/soap/envelope/",
22
+ "xmlns:wst" => "http://docs.oasis-open.org/ws-sx/ws-trust/200512",
23
+ "xmlns:u" => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
24
+ "xmlns:x" => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
25
+ }
26
+
27
+ # Provides the connection details for the SSO service.
28
+ class Connection
29
+
30
+ attr_accessor :sso_url, :wsdl_url, :username, :password
31
+
32
+ # Creates a new instance.
33
+ def initialize(sso_url, wsdl_url=nil)
34
+ self.sso_url = sso_url
35
+ self.wsdl_url = wsdl_url || "#{sso_url}?wsdl"
36
+ end
37
+
38
+ # Login with the given credentials.
39
+ # Note: this does not invoke a login action, but rather stores the
40
+ # credentials for use later.
41
+ def login(username, password)
42
+ self.username = username
43
+ self.password = password
44
+ self # enable builder pattern
45
+ end
46
+
47
+ # Gets (or creates) the Savon client instance.
48
+ def client
49
+ # construct and init the client proxy
50
+ @client ||= Savon.client do |globals|
51
+ # see: http://savonrb.com/version2/globals.html
52
+ globals.wsdl wsdl_url
53
+ globals.endpoint sso_url
54
+
55
+ globals.strip_namespaces false
56
+ globals.env_namespace :S
57
+
58
+ # set like this so https connection does not fail
59
+ # TODO: find an acceptable solution for production
60
+ globals.ssl_verify_mode :none
61
+
62
+ # dev/debug settings
63
+ # globals.pretty_print_xml ENV['DEBUG_SOAP']
64
+ # globals.log ENV['DEBUG_SOAP']
65
+ end
66
+ end
67
+
68
+ # Invokes the request bearer token operation.
69
+ # @return [SamlToken]
70
+ def request_bearer_token()
71
+ rst = RequestSecurityToken.new(client, username, password)
72
+ rst.invoke()
73
+ rst.saml_token
74
+ end
75
+ end
76
+
77
+ # @abstract Base class for invocable service calls.
78
+ class SoapInvocable
79
+
80
+ attr_reader :operation, :client, :response
81
+
82
+ # Constructs a new instance.
83
+ # @param operation [Symbol] the SOAP operation name (in Symbol form)
84
+ # @param client [Savon::Client] the client
85
+ def initialize(operation, client)
86
+ @operation = operation
87
+ @client = client
88
+ end
89
+
90
+ # Invokes the service call represented by this type.
91
+ def invoke
92
+ request = request_xml.to_s
93
+ puts "request = #{request}" if ENV['DEBUG']
94
+ @response = client.call(operation, xml:request)
95
+ puts "response = #{response}" if ENV['DEBUG']
96
+ self # for chaining with new
97
+ end
98
+
99
+ # Builds the request XML content.
100
+ def request_xml
101
+ builder = Builder::XmlMarkup.new()
102
+ builder.instruct!(:xml, encoding: "UTF-8")
103
+
104
+ builder.tag!("S:Envelope", NAMESPACES) do |envelope|
105
+ if has_header?
106
+ envelope.tag!("S:Header") do |header|
107
+ header_xml(header)
108
+ end
109
+ end
110
+ envelope.tag!("S:Body") do |body|
111
+ body_xml(body)
112
+ end
113
+ end
114
+ builder.target!
115
+ end
116
+
117
+ def has_header?
118
+ true
119
+ end
120
+
121
+ # Builds the header portion of the SOAP request.
122
+ # Specific service operations must override this method.
123
+ def header_xml(header)
124
+ raise 'abstract method not implemented!'
125
+ end
126
+
127
+ # Builds the body portion of the SOAP request.
128
+ # Specific service operations must override this method.
129
+ def body_xml(body)
130
+ raise 'abstract method not implemented!'
131
+ end
132
+
133
+ # Gets the response XML content.
134
+ def response_xml
135
+ raise 'illegal state: response not set yet' if response.nil?
136
+ @response_xml ||= Nokogiri::XML(response.to_xml)
137
+ end
138
+
139
+ def response_hash
140
+ @response_hash ||= response.to_hash
141
+ end
142
+ end
143
+
144
+ # Encapsulates an issue operation that requests a security token
145
+ # from the SSO service.
146
+ class RequestSecurityToken < SoapInvocable
147
+
148
+ attr_accessor :request_type, :delegatable
149
+
150
+ # Constructs a new instance.
151
+ def initialize(client, username, password, hours=2)
152
+ super(:issue, client)
153
+
154
+ @username = username
155
+ @password = password
156
+ @hours = hours
157
+
158
+ #TODO: these things should be configurable, so we can get
159
+ #non-delegatable tokens, HoK tokens, etc.
160
+ @request_type = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue"
161
+ @delegatable = true
162
+ end
163
+
164
+ def now
165
+ @now ||= Time.now.utc.to_datetime
166
+ end
167
+
168
+ def created
169
+ @created ||= now.strftime(DATE_FORMAT)
170
+ end
171
+
172
+ def future
173
+ @future ||= now + (2/24.0) #days (for DateTime math)
174
+ end
175
+
176
+ def expires
177
+ @expires ||= future.strftime(DATE_FORMAT)
178
+ end
179
+
180
+ # Builds the header XML for the SOAP request.
181
+ def header_xml(header)
182
+ id = "uuid-" + SecureRandom.uuid
183
+
184
+ #header.tag!("x:Security", "x:mustUnderstand" => "1") do |security|
185
+ header.tag!("x:Security") do |security|
186
+ security.tag!("u:Timestamp", "u:Id" => "_0") do |timestamp|
187
+ timestamp.tag!("u:Created") do |element|
188
+ element << created
189
+ end
190
+ timestamp.tag!("u:Expires") do |element|
191
+ element << expires
192
+ end
193
+ end
194
+ security.tag!("x:UsernameToken", "u:Id" => id) do |utoken|
195
+ utoken.tag!("x:Username") do |element|
196
+ element << @username
197
+ end
198
+ utoken.tag!("x:Password") do |element|
199
+ element << @password
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ # Builds the body XML for the SOAP request.
206
+ def body_xml(body)
207
+ body.tag!("wst:RequestSecurityToken") do |rst|
208
+ rst.tag!("wst:RequestType") do |element|
209
+ element << request_type
210
+ end
211
+ rst.tag!("wst:Delegatable") do |element|
212
+ element << delegatable.to_s
213
+ end
214
+ =begin
215
+ #TODO: we don't seem to need this, but I'm leaving this
216
+ #here for now as a reminder.
217
+ rst.tag!("wst:Lifetime") do |lifetime|
218
+ lifetime.tag!("u:Created") do |element|
219
+ element << created
220
+ end
221
+ lifetime.tag!("u:Expires") do |element|
222
+ element << expires
223
+ end
224
+ end
225
+ =end
226
+ end
227
+ end
228
+
229
+ # Gets the saml_token from the SOAP response body.
230
+ # @return [SamlToken] the requested SAML token
231
+ def saml_token
232
+ assertion = response_xml.at_xpath('//saml2:Assertion',
233
+ 'saml2' => 'urn:oasis:names:tc:SAML:2.0:assertion')
234
+ SamlToken.new(assertion)
235
+ end
236
+ end
237
+
238
+ # Holds a SAML token.
239
+ class SamlToken
240
+ attr_reader :xml
241
+
242
+ # Creates a new instance.
243
+ def initialize(xml)
244
+ @xml = xml
245
+ end
246
+
247
+ #TODO: add some getters for interesting content
248
+
249
+ def to_s
250
+ esc_token = xml.to_xml(:indent => 0, :encoding => 'UTF-8')
251
+ esc_token = esc_token.gsub(/\n/, '')
252
+ esc_token
253
+ end
254
+ end
255
+ end
256
+
257
+ # main: quick self tester
258
+ if __FILE__ == $0
259
+ cloudvm_ip = ARGV[0]
260
+ cloudvm_ip ||= "10.20.17.0"
261
+ #cloudvm_ip ||= "10.67.245.207"
262
+ sso_url = "https://#{cloudvm_ip}/sts/STSService/vsphere.local"
263
+ wsdl_url = "#{sso_url}?wsdl"
264
+ sso = SSO::Connection.new(sso_url, wsdl_url)
265
+ #sso.login("administrator@vsphere.local", "Admin!23")
266
+ sso.login("root", "vmware")
267
+ token = sso.request_bearer_token
268
+ puts token.to_s
269
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
+ # Copyright:: Copyright (c) 2017 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 'rbvmomi'
21
+
22
+ class Support
23
+ class CloneVm
24
+ attr_reader :vim, :options
25
+
26
+ def initialize(conn_opts, options)
27
+ @options = options
28
+
29
+ # Connect to vSphere
30
+ @vim ||= RbVmomi::VIM.connect conn_opts
31
+ end
32
+
33
+ def clone
34
+
35
+ # set the datacenter name
36
+ dc = vim.serviceInstance.find_datacenter(options[:datacenter])
37
+ src_vm = dc.find_vm(options[:template])
38
+ hosts = dc.hostFolder.children
39
+
40
+ # Specify where the machine is going to be created
41
+ relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec
42
+ relocate_spec.host = options[:targethost]
43
+ relocate_spec.pool = hosts.first.resourcePool
44
+
45
+ clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(location: relocate_spec,
46
+ powerOn: options[:poweron],
47
+ template: false)
48
+
49
+ # Set the folder to use
50
+ dest_folder = options[:folder].nil? ? src_vm.parent : options[:folder][:id]
51
+
52
+ puts "Cloning the template to create the new machine..."
53
+ task = src_vm.CloneVM_Task(folder: dest_folder, name: options[:name], spec: clone_spec)
54
+ # TODO: it would be nice to have dots to tell you it's working here
55
+ task.wait_for_completion
56
+
57
+
58
+ # get the IP address of the machine for bootstrapping
59
+ # machine name is based on the path, e.g. that includes the folder
60
+ name = options[:folder].nil? ? options[:name] : format("%s/%s", options[:folder][:name], options[:name])
61
+ new_vm = dc.find_vm(name)
62
+
63
+ if new_vm.nil?
64
+ puts format("Unable to find machine: %s", name)
65
+ else
66
+ puts 'Waiting for network interfaces to become available...'
67
+ sleep 2 while new_vm.guest.net.empty? || !new_vm.guest.ipAddress
68
+ new_vm.guest.net[0].ipConfig.ipAddress.detect do |addr|
69
+ addr.origin != 'linklayer'
70
+ end.ipAddress
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
+ # Copyright:: Copyright (c) 2017 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
+ #
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
+ # Copyright:: Copyright (c) 2017 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 'spec_helper'
21
+ require 'chef/knife/vcenter_vm_list'
22
+ require 'support/shared_examples_for_command'
23
+
24
+ class PowerStatus < BasicObject
25
+ attr_reader :value
26
+ def initialize(state)
27
+ @value = state
28
+ end
29
+ end
30
+
31
+ describe Chef::Knife::Cloud::VcenterVmList do
32
+ it_behaves_like Chef::Knife::Cloud::Command, Chef::Knife::Cloud::VcenterVmList.new
33
+
34
+ subject { described_class.new }
35
+
36
+ describe '#format_power_status' do
37
+ context 'when the power is "POWERED_ON"' do
38
+ it 'displays with green' do
39
+ expect(subject.ui).to receive(:color).with('POWERED_ON', :green)
40
+ subject.format_power_status(PowerStatus.new('POWERED_ON'))
41
+ end
42
+ end
43
+
44
+ context 'when the power is "POWERED_OFF"' do
45
+ it 'displays with red' do
46
+ expect(subject.ui).to receive(:color).with('POWERED_OFF', :red)
47
+ subject.format_power_status(PowerStatus.new('POWERED_OFF'))
48
+ end
49
+ end
50
+
51
+ context 'when the power is "SUSPENDED"' do
52
+ it 'displays with red' do
53
+ expect(subject.ui).to receive(:color).with('SUSPENDED', :yellow)
54
+ subject.format_power_status(PowerStatus.new('SUSPENDED'))
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#format_memory_value' do
60
+ context 'when the memory value is 8192' do
61
+ it 'displays as 8,192' do
62
+ expect(subject.ui).to receive(:text).with('8,192')
63
+ subject.format_memory_value(8192)
64
+ end
65
+ end
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,257 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-vcenter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chef Partner Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: knife-cloud
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rb-readline
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rbvmomi
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.11'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: savon
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.11'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: vsphere-automation-sdk
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.5'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: debase
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: github_changelog_generator
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: pry
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '10.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '10.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.35'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.35'
181
+ - !ruby/object:Gem::Dependency
182
+ name: ruby-debug-ide
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 0.6.0
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 0.6.0
195
+ description: Knife plugin to VMware vCenter.
196
+ email:
197
+ - partnereng@chef.io
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - ".github/ISSUE_TEMPLATE.md"
203
+ - ".github/PULL_REQUEST_TEMPLATE.md"
204
+ - ".gitignore"
205
+ - ".rubocop.yml"
206
+ - ".travis.yml"
207
+ - CHANGELOG.md
208
+ - Gemfile
209
+ - LICENSE.txt
210
+ - README.md
211
+ - Rakefile
212
+ - knife-vcenter.gemspec
213
+ - lib/base.rb
214
+ - lib/chef/knife/cloud/vcenter_service.rb
215
+ - lib/chef/knife/cloud/vcenter_service_helpers.rb
216
+ - lib/chef/knife/cloud/vcenter_service_options.rb
217
+ - lib/chef/knife/vcenter_cluster_list.rb
218
+ - lib/chef/knife/vcenter_datacenter_list.rb
219
+ - lib/chef/knife/vcenter_host_list.rb
220
+ - lib/chef/knife/vcenter_vm_clone.rb
221
+ - lib/chef/knife/vcenter_vm_create.rb
222
+ - lib/chef/knife/vcenter_vm_delete.rb
223
+ - lib/chef/knife/vcenter_vm_list.rb
224
+ - lib/chef/knife/vcenter_vm_show.rb
225
+ - lib/knife-vcenter/version.rb
226
+ - lib/lookup_service_helper.rb
227
+ - lib/sso.rb
228
+ - lib/support/clone_vm.rb
229
+ - spec/spec_helper.rb
230
+ - spec/unit/vcenter_vm_list_spec.rb
231
+ homepage: https://github.com/chef/knife-vcenter
232
+ licenses:
233
+ - Apache 2.0
234
+ metadata: {}
235
+ post_install_message:
236
+ rdoc_options: []
237
+ require_paths:
238
+ - lib
239
+ required_ruby_version: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ required_rubygems_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ requirements: []
250
+ rubyforge_project:
251
+ rubygems_version: 2.6.11
252
+ signing_key:
253
+ specification_version: 4
254
+ summary: Knife plugin to VMware vCenter.
255
+ test_files:
256
+ - spec/spec_helper.rb
257
+ - spec/unit/vcenter_vm_list_spec.rb