knife-vcenter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
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 'chef/knife'
21
+ require 'chef/knife/cloud/server/show_options'
22
+ require 'chef/knife/cloud/server/show_command'
23
+ require 'chef/knife/cloud/vcenter_service'
24
+ require 'chef/knife/cloud/vcenter_service_helpers'
25
+ require 'chef/knife/cloud/vcenter_service_options'
26
+
27
+ class Chef
28
+ class Knife
29
+ class Cloud
30
+ class VcenterVmShow < ServerShowCommand
31
+ include ServerShowOptions
32
+ include VcenterServiceOptions
33
+ include VcenterServiceHelpers
34
+
35
+ banner 'knife vcenter vm show NAME (options)'
36
+
37
+ # rubocop:disable Style/GuardClause
38
+ def validate_params!
39
+ if @name_args.empty?
40
+ ui.error('You must supply the name of the virtual machine to display.')
41
+ exit(1) if @name_args.empty?
42
+ end
43
+
44
+ if @name_args.size > 1
45
+ ui.error('You may only supply one virtual machine name')
46
+ exit 1
47
+ end
48
+
49
+ super
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,22 @@
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
+ module KnifeVcenter
21
+ VERSION = '1.0.0'.freeze
22
+ end
@@ -0,0 +1,463 @@
1
+ # Copyright 2014-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'savon'
5
+ require 'nokogiri'
6
+ require 'base'
7
+ # require 'sample/framework/sample_base'
8
+
9
+ # Utility class that helps use the lookup service.
10
+ class LookupServiceHelper
11
+
12
+ attr_reader :sample, :wsdl_url, :soap_url
13
+ attr_reader :serviceRegistration
14
+
15
+ # Constructs a new instance.
16
+ # @param sample [SampleBase] the associated sample, which provides access
17
+ # to the configuration properties of the sample
18
+ def initialize(host)
19
+
20
+ @soap_url = format("https://%s/lookupservice/sdk", host)
21
+ @wsdl_url = format("https://%s/lookupservice/wsdl/lookup.wsdl", host)
22
+
23
+ end
24
+
25
+ # Connects to the lookup service.
26
+ def connect
27
+ rsc = RetrieveServiceContent.new(client).invoke()
28
+ @serviceRegistration = rsc.get_service_registration()
29
+ Base.log.info "service registration = #{serviceRegistration}"
30
+ end
31
+
32
+ # Finds the SSO service URL.
33
+ # In a MxN setup where there are more than one PSC nodes;
34
+ # This method returns the first SSO service endpoint URL
35
+ # as returned by the lookup service.
36
+ #
37
+ # @return [String] SSO Service endpoint URL.
38
+ def find_sso_url
39
+ result = find_service_url(product='com.vmware.cis',
40
+ service='cs.identity',
41
+ endpoint='com.vmware.cis.cs.identity.sso',
42
+ protocol='wsTrust')
43
+ raise 'SSO URL not found' unless result && result.size > 0
44
+ return result.values[0]
45
+ end
46
+
47
+ # Finds all the vAPI service endpoint URLs.
48
+ # In a MxN setup where there are more than one management node;
49
+ # this method returns more than one URL
50
+ #
51
+ # @return [Hash] vapi service endpoint URLs in a dictionary
52
+ # where the key is the node_id and the value is the service URL.
53
+ def find_vapi_urls
54
+ return find_service_url(product='com.vmware.cis',
55
+ service='cs.vapi',
56
+ endpoint='com.vmware.vapi.endpoint',
57
+ protocol='vapi.json.https.public')
58
+ end
59
+
60
+ # Finds the vapi service endpoint URL of a management node.
61
+ #
62
+ # @param node_id [String] The UUID of the management node.
63
+ # @return [String] vapi service endpoint URL of a management node or
64
+ # nil if no vapi endpoint is found.
65
+ def find_vapi_url(node_id)
66
+ raise 'node_id is required' if node_id.nil?
67
+ result = find_vapi_urls()
68
+ raise 'VAPI URLs not found' unless result && result.size > 0
69
+ return result[node_id]
70
+ end
71
+
72
+ # Finds all the vim service endpoint URLs
73
+ # In a MxN setup where there are more than one management node;
74
+ # this method returns more than one URL
75
+ #
76
+ # @return [Hash] vim service endpoint URLs in a dictionary where
77
+ # the key is the node_id and the value is the service URL.
78
+ def find_vim_urls
79
+ return find_service_url(product='com.vmware.cis',
80
+ service='vcenterserver',
81
+ endpoint='com.vmware.vim',
82
+ protocol='vmomi')
83
+ end
84
+
85
+ # Finds the vim service endpoint URL of a management node
86
+ #
87
+ # @param node_id [String] The UUID of the management node.
88
+ # @return [String] vim service endpoint URL of a management node or
89
+ # nil if no vim endpoint is found.
90
+ def find_vim_url(node_id)
91
+ raise 'node_id is required' if node_id.nil?
92
+ result = find_vim_urls()
93
+ raise 'VIM URLs not found' unless result && result.size > 0
94
+ return result[node_id]
95
+ end
96
+
97
+ # Finds all the spbm service endpoint URLs
98
+ # In a MxN setup where there are more than one management node;
99
+ # this method returns more than one URL
100
+ #
101
+ # @return [Hash] spbm service endpoint URLs in a dictionary where
102
+ # the key is the node_id and the value is the service URL.
103
+ def find_vim_pbm_urls
104
+ return find_service_url(product='com.vmware.vim.sms',
105
+ service='sms',
106
+ endpoint='com.vmware.vim.pbm',
107
+ protocol='https')
108
+ end
109
+
110
+ # Finds the spbm service endpoint URL of a management node
111
+ #
112
+ # @param node_id [String] The UUID of the management node.
113
+ # @return [String] spbm service endpoint URL of a management node or
114
+ # nil if no spbm endpoint is found.
115
+ def find_vim_pbm_url(node_id)
116
+ raise 'node_id is required' if node_id.nil?
117
+ result = find_vim_pbm_urls()
118
+ raise 'PBM URLs not found' unless result && result.size > 0
119
+ return result[node_id]
120
+ end
121
+
122
+ # Get the management node id from the instance name
123
+ #
124
+ # @param instance_name [String] The instance name of the management node
125
+ # @return [String] The UUID of the management node or
126
+ # nil is no management node is found by the given instance name
127
+ def get_mgmt_node_id(instance_name)
128
+ raise 'instance_name is required' if instance_name.nil?
129
+ result = find_mgmt_nodes()
130
+ raise 'Management nodes not found' unless result && result.size > 0
131
+ return result[instance_name]
132
+ end
133
+
134
+ def get_mgmt_node_instance_name(node_id)
135
+ raise 'node_id is required' if node_id.nil?
136
+ result = find_mgmt_nodes()
137
+ raise 'Management nodes not found' unless result && result.size > 0
138
+ result.each { |k, v| return k if v == node_id }
139
+ nil
140
+ end
141
+
142
+ # Finds the instance name and UUID of the management node for M1xN1 or
143
+ # when the PSC and management services all reside on a single node.
144
+ def get_default_mgmt_node
145
+ result = find_mgmt_nodes()
146
+ raise 'Management nodes not found' unless result && result.size > 0
147
+ #WHY: raise MultipleManagementNodeException.new if result.size > 1
148
+ return [result.keys[0], result.values[0]]
149
+ end
150
+
151
+ # Finds all the management nodes
152
+ #
153
+ # @return [Hash] management node instance name and node id (UUID) in a dictionary.
154
+ def find_mgmt_nodes
155
+ #assert self.serviceRegistration is not None
156
+ list = List.new(client, 'com.vmware.cis', 'vcenterserver',
157
+ 'vmomi', 'com.vmware.vim')
158
+
159
+ list.invoke()
160
+ list.get_instance_names()
161
+ end
162
+
163
+ private
164
+
165
+ # Finds a service URL with the given attributes.
166
+ def find_service_url(product, service, endpoint, protocol)
167
+ #assert serviceRegistration is not None
168
+ list = List.new(client, product, service, protocol, endpoint)
169
+
170
+ list.invoke()
171
+ list.get_service_endpoints()
172
+ end
173
+
174
+ # Gets or creates the Savon client instance.
175
+ def client
176
+ @client ||= Savon.client do |globals|
177
+ # see: http://savonrb.com/version2/globals.html
178
+ globals.wsdl wsdl_url
179
+ globals.endpoint soap_url
180
+
181
+ globals.strip_namespaces false
182
+ globals.env_namespace :S
183
+
184
+ # set like this so https connection does not fail
185
+ # TODO: find an acceptable solution for production
186
+ globals.ssl_verify_mode :none
187
+
188
+ # dev/debug settings
189
+ # globals.pretty_print_xml ENV['DEBUG_SOAP']
190
+ # globals.log ENV['DEBUG_SOAP']
191
+ end
192
+ end
193
+ end
194
+
195
+
196
+ # @abstract Base class for invocable service calls.
197
+ class Invocable
198
+
199
+ attr_reader :operation, :client, :response
200
+
201
+ # Constructs a new instance.
202
+ # @param operation [Symbol] the operation name
203
+ # @param client [Savon::Client] the client
204
+ def initialize(operation, client)
205
+ @operation = operation
206
+ @client = client
207
+ end
208
+
209
+ # Invokes the service call represented by this type.
210
+ def invoke
211
+ request = request_xml.to_s
212
+ Base.log.debug(request)
213
+ @response = client.call(operation, xml:request)
214
+ Base.log.debug(response)
215
+ self # for chaining with new
216
+ end
217
+
218
+ # Builds the request XML content.
219
+ def request_xml
220
+ builder = Builder::XmlMarkup.new()
221
+ builder.instruct!(:xml, encoding: "UTF-8")
222
+
223
+ builder.tag!("S:Envelope",
224
+ "xmlns:S" => "http://schemas.xmlsoap.org/soap/envelope/") do |envelope|
225
+ envelope.tag!("S:Body") do |body|
226
+ body_xml(body)
227
+ end
228
+ end
229
+ builder.target!
230
+ end
231
+
232
+ # Builds the body portion of the request XML content.
233
+ # Specific service operations must override this method.
234
+ def body_xml
235
+ raise 'abstract method not implemented!'
236
+ end
237
+
238
+ # Gets the response XML content.
239
+ def response_xml
240
+ raise 'illegal state: response not set yet' if response.nil?
241
+ @response_xml ||= Nokogiri::XML(response.to_xml)
242
+ end
243
+
244
+ def response_hash
245
+ @response_hash ||= response.to_hash
246
+ end
247
+ end
248
+
249
+ # Encapsulates the list operation of the lookup service.
250
+ class List < Invocable
251
+
252
+ # Constructs a new instance.
253
+ def initialize(client, product, service, protocol, endpoint)
254
+ super(:list, client)
255
+
256
+ @product = product
257
+ @service = service
258
+ @protocol = protocol
259
+ @endpoint = endpoint
260
+ end
261
+
262
+ =begin
263
+ <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
264
+ <S:Body>
265
+ <List xmlns="urn:lookup">
266
+ <_this type="LookupServiceRegistration">ServiceRegistration</_this>
267
+ <filterCriteria>
268
+ <serviceType>
269
+ <product>com.vmware.cis</product>
270
+ <type>cs.identity</type>
271
+ </serviceType>
272
+ <endpointType>
273
+ <protocol>wsTrust</protocol>
274
+ <type>com.vmware.cis.cs.identity.sso</type>
275
+ </endpointType>
276
+ </filterCriteria>
277
+ </List>
278
+ </S:Body>
279
+ </S:Envelope>
280
+ =end
281
+ def body_xml(body)
282
+ body.tag!("List", "xmlns" => "urn:lookup") do |list|
283
+ #TODO: use the copy that was retrieved on startup?
284
+ list.tag!("_this",
285
+ "type" => "LookupServiceRegistration") do |this|
286
+ this << "ServiceRegistration"
287
+ end
288
+ list.tag!("filterCriteria") do |criteria|
289
+ criteria.tag!("serviceType") do |stype|
290
+ stype.tag!("product") do |p|
291
+ p << @product
292
+ end
293
+ stype.tag!("type") do |t|
294
+ t << @service
295
+ end
296
+ end
297
+ criteria.tag!("endpointType") do |etype|
298
+ etype.tag!("protocol") do |p|
299
+ p << @protocol
300
+ end
301
+ etype.tag!("type") do |t|
302
+ t << @endpoint
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ # Gets the service endpoint information from the response.
310
+ # Support for MxN.
311
+ # @return [Hash] a hash where the key is NodeId and the Value is a Service URL
312
+ def get_service_endpoints
313
+ result = {}
314
+ =begin
315
+ <ListResponse xmlns="urn:lookup">
316
+ <returnval>
317
+ <serviceVersion>2.0</serviceVersion>
318
+ <vendorNameResourceKey/>
319
+ <vendorNameDefault/>
320
+ <vendorProductInfoResourceKey/>
321
+ <vendorProductInfoDefault/>
322
+ <serviceEndpoints>
323
+ <url>https://pa-rdinfra3-vm7-dhcp5583.eng.vmware.com/sts/STSService/vsphere.local</url>
324
+ <endpointType>
325
+ <protocol>wsTrust</protocol>
326
+ <type>com.vmware.cis.cs.identity.sso</type>
327
+ </endpointType>
328
+ <sslTrust>
329
+ ...
330
+ </sslTrust>
331
+ </serviceEndpoints>
332
+ <serviceNameResourceKey/>
333
+ <serviceNameDefault/>
334
+ <serviceDescriptionResourceKey/>
335
+ <serviceDescriptionDefault/>
336
+ <ownerId>pa-rdinfra3-vm7-dhcp5583.eng.vmware.com@vsphere.local</ownerId>
337
+ <serviceType>
338
+ <product>com.vmware.cis</product>
339
+ <type>cs.identity</type>
340
+ </serviceType>
341
+ <nodeId/>
342
+ <serviceId>6a8a5058-5d3d-4d42-bb5e-383b91c8732e</serviceId>
343
+ <siteId>default-first-site</siteId>
344
+ </returnval>
345
+ </ListResponse>
346
+ =end
347
+ Base.log.debug "List: response_hash = #{response_hash}"
348
+ return_val = response_hash[:list_response][:returnval]
349
+ return_val = [return_val] if return_val.is_a? Hash
350
+ return_val.each { |entry|
351
+ #FYI: the node_id is sometimes null, so use the service_id in this case
352
+ node_id = entry[:node_id] || entry[:service_id]
353
+ result[node_id] = entry[:service_endpoints][:url]
354
+ }
355
+ Base.log.debug "List: result = #{result}"
356
+ return result
357
+ end
358
+
359
+ def get_instance_names
360
+ result = {}
361
+ =begin
362
+ <serviceAttributes>
363
+ <key>com.vmware.cis.cm.GroupInternalId</key>
364
+ <value>com.vmware.vim.vcenter</value>
365
+ </serviceAttributes>
366
+ <serviceAttributes>
367
+ <key>com.vmware.cis.cm.ControlScript</key>
368
+ <value>vmware-vpxd.sh</value>
369
+ </serviceAttributes>
370
+ <serviceAttributes>
371
+ <key>com.vmware.cis.cm.HostId</key>
372
+ <value>906477a1-24c6-4d48-9e99-55ef962878f7</value>
373
+ </serviceAttributes>
374
+ <serviceAttributes>
375
+ <key>com.vmware.vim.vcenter.instanceName</key>
376
+ <value>pa-rdinfra3-vm7-dhcp5583.eng.vmware.com</value>
377
+ </serviceAttributes>
378
+ =end
379
+ Base.log.debug "List: response_hash = #{response_hash}"
380
+ return_val = response_hash[:list_response][:returnval]
381
+ return_val = [return_val] if return_val.is_a? Hash
382
+ return_val.each { |entry|
383
+ node_id = entry[:node_id]
384
+ #TODO: is it possible there be 0 or 1 attrs? if so, deal with it.
385
+ attrs = entry[:service_attributes]
386
+ Base.log.debug "List: attrs=#{attrs}"
387
+ attrs.each { |attr|
388
+ if attr[:key] == 'com.vmware.vim.vcenter.instanceName'
389
+ result[attr[:value]] = node_id
390
+ end
391
+ }
392
+ }
393
+ Base.log.debug "List: result = #{result}"
394
+ return result
395
+ end
396
+ end
397
+
398
+ # Encapsulates the RetrieveServiceContent operation of the lookup service.
399
+ class RetrieveServiceContent < Invocable
400
+
401
+ # Constructs a new instance.
402
+ def initialize(client)
403
+ super(:retrieve_service_content, client)
404
+ end
405
+
406
+ =begin
407
+ <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
408
+ <S:Body>
409
+ <RetrieveServiceContent xmlns="urn:lookup">
410
+ <_this type="LookupServiceInstance">ServiceInstance</_this>
411
+ </RetrieveServiceContent>
412
+ </S:Body>
413
+ </S:Envelope>
414
+ =end
415
+ def body_xml(body)
416
+ body.tag!("RetrieveServiceContent", "xmlns" => "urn:lookup") do |rsc|
417
+ rsc.tag!("_this", "type" => "LookupServiceInstance") do |this|
418
+ this << "ServiceInstance"
419
+ end
420
+ end
421
+ end
422
+
423
+ =begin
424
+ ...
425
+ <RetrieveServiceContentResponse xmlns="urn:lookup">
426
+ <returnval>
427
+ <lookupService type="LookupLookupService">lookupService</lookupService>
428
+ <serviceRegistration type="LookupServiceRegistration">ServiceRegistration</serviceRegistration>
429
+ <deploymentInformationService type="LookupDeploymentInformationService">deploymentInformationService</deploymentInformationService>
430
+ <l10n type="LookupL10n">l10n</l10n>
431
+ </returnval>
432
+ </RetrieveServiceContentResponse>
433
+ ...
434
+ =end
435
+ def get_service_registration
436
+ Base.log.debug "RetrieveServiceContent: response_hash = #{response_hash}"
437
+ return_val = response_hash[:retrieve_service_content_response][:returnval]
438
+ result = return_val[:service_registration]
439
+ Base.log.debug "RetrieveServiceContent: result = #{result}"
440
+ result
441
+ end
442
+ end
443
+
444
+ class MultipleManagementNodeException < Exception
445
+ end
446
+
447
+ # main: quick self tester
448
+ if __FILE__ == $0
449
+ Base.log.level = Logger::DEBUG if ENV['DEBUG']
450
+ sample = SelfTestSample.new
451
+ sample.ls_ip = ARGV[0] || '10.67.245.207'
452
+ #MXN: sample.ls_ip = '10.160.42.83'
453
+ #MXN: sample.ls_ip = '10.160.35.191'
454
+ #MAYBE: sample.main() # for arg parsing
455
+ ls_helper = LookupServiceHelper.new(sample)
456
+ ls_helper.connect()
457
+ puts '***************************************'
458
+ puts "SSO URL: #{ls_helper.find_sso_url()}"
459
+ puts "VAPI URL: #{ls_helper.find_vapi_urls()}"
460
+ puts "VIM URL: #{ls_helper.find_vim_urls()}"
461
+ puts "PBM URL: #{ls_helper.find_vim_pbm_urls()}"
462
+ puts "Mgmt Nodes: #{ls_helper.find_mgmt_nodes()}"
463
+ end