knife-cosmic 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.rdoc +186 -0
  3. data/LICENSE +202 -0
  4. data/README.rdoc +427 -0
  5. data/lib/chef/knife/cosmic_aag_list.rb +58 -0
  6. data/lib/chef/knife/cosmic_account_list.rb +87 -0
  7. data/lib/chef/knife/cosmic_base.rb +108 -0
  8. data/lib/chef/knife/cosmic_baselist.rb +111 -0
  9. data/lib/chef/knife/cosmic_cluster_list.rb +60 -0
  10. data/lib/chef/knife/cosmic_config_list.rb +56 -0
  11. data/lib/chef/knife/cosmic_disk_list.rb +58 -0
  12. data/lib/chef/knife/cosmic_domain_list.rb +53 -0
  13. data/lib/chef/knife/cosmic_firewallrule_create.rb +138 -0
  14. data/lib/chef/knife/cosmic_firewallrule_list.rb +62 -0
  15. data/lib/chef/knife/cosmic_forwardrule_create.rb +145 -0
  16. data/lib/chef/knife/cosmic_host_list.rb +61 -0
  17. data/lib/chef/knife/cosmic_hosts.rb +58 -0
  18. data/lib/chef/knife/cosmic_iso_list.rb +89 -0
  19. data/lib/chef/knife/cosmic_keypair_create.rb +72 -0
  20. data/lib/chef/knife/cosmic_keypair_delete.rb +60 -0
  21. data/lib/chef/knife/cosmic_keypair_list.rb +44 -0
  22. data/lib/chef/knife/cosmic_network_list.rb +63 -0
  23. data/lib/chef/knife/cosmic_oscategory_list.rb +50 -0
  24. data/lib/chef/knife/cosmic_ostype_list.rb +52 -0
  25. data/lib/chef/knife/cosmic_pod_list.rb +60 -0
  26. data/lib/chef/knife/cosmic_project_list.rb +63 -0
  27. data/lib/chef/knife/cosmic_publicip_list.rb +55 -0
  28. data/lib/chef/knife/cosmic_router_list.rb +64 -0
  29. data/lib/chef/knife/cosmic_securitygroup_list.rb +59 -0
  30. data/lib/chef/knife/cosmic_server_add_nic.rb +109 -0
  31. data/lib/chef/knife/cosmic_server_create.rb +674 -0
  32. data/lib/chef/knife/cosmic_server_delete.rb +153 -0
  33. data/lib/chef/knife/cosmic_server_list.rb +167 -0
  34. data/lib/chef/knife/cosmic_server_passwordreset.rb +91 -0
  35. data/lib/chef/knife/cosmic_server_reboot.rb +99 -0
  36. data/lib/chef/knife/cosmic_server_remove_nic.rb +101 -0
  37. data/lib/chef/knife/cosmic_server_start.rb +104 -0
  38. data/lib/chef/knife/cosmic_server_stop.rb +118 -0
  39. data/lib/chef/knife/cosmic_server_update.rb +47 -0
  40. data/lib/chef/knife/cosmic_service_list.rb +74 -0
  41. data/lib/chef/knife/cosmic_stack_create.rb +298 -0
  42. data/lib/chef/knife/cosmic_stack_delete.rb +79 -0
  43. data/lib/chef/knife/cosmic_template_create.rb +129 -0
  44. data/lib/chef/knife/cosmic_template_extract.rb +104 -0
  45. data/lib/chef/knife/cosmic_template_list.rb +88 -0
  46. data/lib/chef/knife/cosmic_template_register.rb +187 -0
  47. data/lib/chef/knife/cosmic_user_list.rb +62 -0
  48. data/lib/chef/knife/cosmic_volume_attach.rb +70 -0
  49. data/lib/chef/knife/cosmic_volume_create.rb +108 -0
  50. data/lib/chef/knife/cosmic_volume_delete.rb +97 -0
  51. data/lib/chef/knife/cosmic_volume_detach.rb +61 -0
  52. data/lib/chef/knife/cosmic_volume_list.rb +77 -0
  53. data/lib/chef/knife/cosmic_zone_list.rb +53 -0
  54. data/lib/knife-cosmic/connection.rb +1046 -0
  55. metadata +127 -0
@@ -0,0 +1,53 @@
1
+ #
2
+ # Original knife-cloudstack author:: Ryan Holmes (<rholmes@edmunds.com>)
3
+ # Original knife-cloudstack author:: Sander Botman (<sbotman@schubergphilis.com>)
4
+ # Copyright:: Copyright (c) 2011 Edmunds, Inc.
5
+ # Copyright:: Copyright (c) 2013 Sander Botman.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'chef/knife'
22
+ require 'chef/knife/cosmic_baselist'
23
+
24
+ module Knifecosmic
25
+ class CosmicZoneList < Chef::Knife
26
+
27
+ include Chef::Knife::KnifecosmicBaseList
28
+
29
+ banner "knife cosmic zone list (options)"
30
+
31
+ option :keyword,
32
+ :long => "--keyword KEY",
33
+ :description => "List by keyword"
34
+
35
+ def run
36
+ validate_base_options
37
+
38
+ columns = [
39
+ 'Name :name',
40
+ 'Network Type :networktype',
41
+ 'Security Groups :securitygroupsenabled'
42
+ ]
43
+
44
+ params = { 'command' => "listZones" }
45
+ params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
46
+ params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
47
+
48
+ result = connection.list_object(params, "zone")
49
+ list_object(columns, result)
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,1046 @@
1
+ #
2
+ # knife-cosmic author:: Robbert-Jan Sperna Weiland (<rspernaweiland@schubergphilis.com>)
3
+ # Original knife-cloudstack author:: Ryan Holmes (<rholmes@edmunds.com>)
4
+ # Original knife-cloudstack author:: KC Braunschweig (<kcbraunschweig@gmail.com>)
5
+ # Original knife-cloudstack author:: Sander Botman (<sbotman@schubergphilis.com>)
6
+ # Original knife-cloudstack author:: Frank Breedijk (<fbreedijk@schubergphilis.com>)
7
+ # Original knife-cloudstack author:: Sander van Harmelen (<svanharmelen@schubergphilis.com>)
8
+ # Copyright:: Copyright (c) 2011 Edmunds, Inc.
9
+ # License:: Apache License, Version 2.0
10
+ #
11
+ # Licensed under the Apache License, Version 2.0 (the "License");
12
+ # you may not use this file except in compliance with the License.
13
+ # You may obtain a copy of the License at
14
+ #
15
+ # http://www.apache.org/licenses/LICENSE-2.0
16
+ #
17
+ # Unless required by applicable law or agreed to in writing, software
18
+ # distributed under the License is distributed on an "AS IS" BASIS,
19
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ # See the License for the specific language governing permissions and
21
+ # limitations under the License.
22
+ #
23
+
24
+ require 'rubygems'
25
+ require 'base64'
26
+ require 'openssl'
27
+ require 'uri'
28
+ require 'cgi'
29
+ require 'net/http'
30
+ require 'json'
31
+ require 'highline/import'
32
+
33
+ class String
34
+ def to_regexp
35
+ return nil unless self.strip.match(/\A\/(.*)\/(.*)\Z/mx)
36
+ regexp , flags = $1 , $2
37
+ return nil if !regexp || flags =~ /[^xim]/m
38
+
39
+ x = /x/.match(flags) && Regexp::EXTENDED
40
+ i = /i/.match(flags) && Regexp::IGNORECASE
41
+ m = /m/.match(flags) && Regexp::MULTILINE
42
+
43
+ Regexp.new regexp , [x,i,m].inject(0){|a,f| f ? a+f : a }
44
+ end
45
+
46
+ def is_uuid?
47
+ self.strip =~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/ ? true : false
48
+ end
49
+ end
50
+
51
+ module CosmicClient
52
+ class Connection
53
+
54
+ ASYNC_POLL_INTERVAL = 5.0
55
+ ASYNC_TIMEOUT = 3600
56
+
57
+ def initialize(api_url, api_key, secret_key, project_name=nil, no_ssl_verify=false, api_proxy=nil)
58
+ @api_url = api_url
59
+ @api_key = api_key
60
+ @secret_key = secret_key
61
+ @no_ssl_verify = no_ssl_verify
62
+ @project_id = get_project_id(project_name) if project_name || nil
63
+ @api_proxy = api_proxy
64
+ end
65
+
66
+ ##
67
+ # Get project id
68
+ def get_project_id(name)
69
+ project = get_project(name)
70
+ if !project then
71
+ puts "Project #{project_name} does not exist"
72
+ exit 1
73
+ end
74
+ project['id']
75
+ end
76
+
77
+ ##
78
+ # Finds the server with the specified name.
79
+
80
+ def get_server(name)
81
+ params = {
82
+ 'command' => 'listVirtualMachines',
83
+ 'name' => name
84
+ }
85
+ json = send_request(params)
86
+ machines = json['virtualmachine']
87
+
88
+ if !machines || machines.empty? then
89
+ return nil
90
+ end
91
+ machine = machines.select { |item| name == item['name'] }
92
+ machine.first
93
+ end
94
+
95
+ ##
96
+ # Finds the public ip for a server
97
+
98
+ def get_server_public_ip(server, cached_rules=nil, cached_nat=nil)
99
+ return nil unless server
100
+ # find the public ip
101
+ nic = get_server_default_nic(server)
102
+
103
+ ssh_rule = get_ssh_port_forwarding_rule(server, cached_rules)
104
+ return ssh_rule['ipaddress'] if ssh_rule
105
+
106
+ winrm_rule = get_winrm_port_forwarding_rule(server, cached_rules)
107
+ return winrm_rule['ipaddress'] if winrm_rule
108
+
109
+ #check for static NAT
110
+ if cached_nat
111
+ ip_addr = cached_nat.find {|v| v['virtualmachineid'] == server['id']}
112
+ else
113
+ ip_addr = list_public_ip_addresses.find {|v| v['virtualmachineid'] == server['id']}
114
+ end
115
+ if ip_addr
116
+ return ip_addr['ipaddress']
117
+ end
118
+ if nic.empty?
119
+ return []
120
+ end
121
+ nic['ipaddress'] || []
122
+ end
123
+
124
+ ##
125
+ # Returns the fully qualified domain name for a server.
126
+
127
+ def get_server_fqdn(server)
128
+ return nil unless server
129
+
130
+ nic = get_server_default_nic(server) || {}
131
+ networks = list_networks || {}
132
+
133
+ id = nic['networkid']
134
+ network = networks.select { |net|
135
+ net['id'] == id
136
+ }.first
137
+ return nil unless network
138
+
139
+ "#{server['name']}.#{network['networkdomain']}"
140
+ end
141
+
142
+ def get_server_default_nic(server)
143
+ server['nic'].each do |nic|
144
+ return nic if nic['isdefault']
145
+ end
146
+ end
147
+
148
+ ##
149
+ # List all the objects based on the command that is specified.
150
+
151
+ def list_object(params, json_result)
152
+ json = send_request(params)
153
+ Chef::Log.debug("JSON (list_object) result: #{json}")
154
+
155
+ result = json["#{json_result}"] || []
156
+ result = data_filter(result, params['filter']) if params['filter']
157
+ result
158
+ end
159
+
160
+ ##
161
+ # Lists all the servers in your account.
162
+
163
+ def list_servers
164
+ params = {
165
+ 'command' => 'listVirtualMachines'
166
+ }
167
+ json = send_request(params)
168
+ json['virtualmachine'] || []
169
+ end
170
+
171
+ ## Updates a server
172
+ def server_update(params)
173
+ params['command'] = 'updateVirtualMachine'
174
+
175
+ json = send_request(params)
176
+ json
177
+ end
178
+
179
+
180
+ ##
181
+ # Deploys a new server using the specified parameters.
182
+
183
+ def create_server(host_name, service_name, template_name, disk_name=nil, zone_name=nil, network_names=[], extra_params)
184
+
185
+ if host_name then
186
+ if get_server(host_name) then
187
+ puts "\nError: Server '#{host_name}' already exists."
188
+ exit 1
189
+ end
190
+ end
191
+
192
+ service = get_service_offering(service_name)
193
+ if !service then
194
+ puts "\nError: Service offering '#{service_name}' is invalid"
195
+ exit 1
196
+ end
197
+
198
+ template = get_template(template_name, zone_name)
199
+ template = get_iso(template_name, zone_name) unless template
200
+
201
+ if !template then
202
+ puts "\nError: Template / ISO name: '#{template_name}' is invalid"
203
+ exit 1
204
+ end
205
+
206
+ if disk_name then
207
+ disk = get_disk_offering(disk_name)
208
+ if !disk then
209
+ puts "\nError: Disk offering '#{disk_name}' is invalid"
210
+ exit 1
211
+ end
212
+ end
213
+
214
+ zone = zone_name ? get_zone(zone_name) : get_default_zone
215
+ if !zone then
216
+ msg = zone_name ? "Zone '#{zone_name}' is invalid" : "No default zone found"
217
+ puts "\nError: #{msg}"
218
+ exit 1
219
+ end
220
+
221
+ if zone['networktype'] != 'Basic' then
222
+ # If this is a Basic zone no networkids are needed in the params
223
+
224
+ networks = []
225
+ if network_names.nil? then
226
+ networks << get_default_network(zone['id'])
227
+ else
228
+ network_names.each do |name|
229
+ network = get_network(name)
230
+ if !network then
231
+ puts "\nError: Network '#{name}' not found"
232
+ end
233
+ networks << get_network(name)
234
+ end
235
+ end
236
+
237
+ if networks.empty? then
238
+ networks << get_default_network(zone['id'])
239
+ end
240
+ if networks.empty? then
241
+ puts "\nError: No default network found"
242
+ exit 1
243
+ end
244
+ network_ids = networks.map { |network|
245
+ network['id']
246
+ }
247
+
248
+ params = {
249
+ 'command' => 'deployVirtualMachine',
250
+ 'serviceOfferingId' => service['id'],
251
+ 'templateId' => template['id'],
252
+ 'zoneId' => zone['id'],
253
+ 'networkids' => network_ids.join(',')
254
+ }
255
+
256
+ else
257
+
258
+ params = {
259
+ 'command' => 'deployVirtualMachine',
260
+ 'serviceOfferingId' => service['id'],
261
+ 'templateId' => template['id'],
262
+ 'zoneId' => zone['id']
263
+ }
264
+
265
+ end
266
+
267
+ params.merge!(extra_params) if extra_params
268
+
269
+ params['name'] = host_name if host_name
270
+ params['diskOfferingId'] = disk['id'] if disk
271
+
272
+ json = send_async_request(params)
273
+ json['virtualmachine']
274
+ end
275
+
276
+ ##
277
+ # Deletes the server with the specified name.
278
+ #
279
+
280
+ def delete_server(name, expunge)
281
+ server = get_server(name)
282
+ if !server || !server['id'] then
283
+ puts "\nError: Virtual machine '#{name}' does not exist"
284
+ exit 1
285
+ end
286
+
287
+ params = {
288
+ 'command' => 'destroyVirtualMachine',
289
+ 'id' => server['id'],
290
+ 'expunge' => expunge
291
+ }
292
+
293
+ json = send_async_request(params)
294
+ json['virtualmachine']
295
+ end
296
+
297
+ ##
298
+ # Stops the server with the specified name.
299
+ #
300
+
301
+ def stop_server(name, forced=nil)
302
+ server = get_server(name)
303
+ if !server || !server['id'] then
304
+ puts "\nError: Virtual machine '#{name}' does not exist"
305
+ exit 1
306
+ end
307
+
308
+ params = {
309
+ 'command' => 'stopVirtualMachine',
310
+ 'id' => server['id']
311
+ }
312
+ params['forced'] = true if forced
313
+
314
+ json = send_async_request(params)
315
+ json['virtualmachine']
316
+ end
317
+
318
+ ##
319
+ # Resets root password for server with the specified name.
320
+ #
321
+
322
+ def server_passwordreset(name)
323
+ server = get_server(name)
324
+ if !server || !server['id'] then
325
+ puts "\nError: Virtual machine '#{name}' does not exist"
326
+ exit 1
327
+ end
328
+
329
+ params = {
330
+ 'command' => 'resetPasswordForVirtualMachine',
331
+ 'id' => server['id']
332
+ }
333
+
334
+ json = send_async_request(params)
335
+ json['virtualmachine']
336
+ end
337
+
338
+ ##
339
+ # Start the server with the specified name.
340
+ #
341
+
342
+ def start_server(name)
343
+ server = get_server(name)
344
+ if !server || !server['id'] then
345
+ puts "\nError: Virtual machine '#{name}' does not exist"
346
+ exit 1
347
+ end
348
+
349
+ params = {
350
+ 'command' => 'startVirtualMachine',
351
+ 'id' => server['id']
352
+ }
353
+
354
+ json = send_async_request(params)
355
+ json['virtualmachine']
356
+ end
357
+
358
+ ##
359
+ # Reboot the server with the specified name.
360
+ #
361
+
362
+ def reboot_server(name)
363
+ server = get_server(name)
364
+ if !server || !server['id'] then
365
+ puts "\nError: Virtual machine '#{name}' does not exist"
366
+ exit 1
367
+ end
368
+
369
+ params = {
370
+ 'command' => 'rebootVirtualMachine',
371
+ 'id' => server['id']
372
+ }
373
+
374
+ json = send_async_request(params)
375
+ json['virtualmachine']
376
+ end
377
+
378
+ ##
379
+ # Finds the service offering with the specified name.
380
+
381
+ def get_service_offering(name)
382
+
383
+ # TODO: use name parameter
384
+ # listServiceOfferings in cosmic 2.2 doesn't seem to work
385
+ # when the name parameter is specified. When this is fixed,
386
+ # the name parameter should be added to the request.
387
+ params = {
388
+ 'command' => 'listServiceOfferings'
389
+ }
390
+ json = send_request(params)
391
+
392
+ services = json['serviceoffering']
393
+ return nil unless services
394
+
395
+ services.each { |s|
396
+ if name.is_uuid? then
397
+ return s if s['id'] == name
398
+ else
399
+ return s if s['name'] == name
400
+ end
401
+ }
402
+ nil
403
+ end
404
+
405
+ ##
406
+ # Lists all available service offerings.
407
+
408
+ def list_service_offerings
409
+ params = {
410
+ 'command' => 'listServiceOfferings'
411
+ }
412
+ json = send_request(params)
413
+ json['serviceoffering'] || []
414
+ end
415
+
416
+ def list_security_groups
417
+ params = {
418
+ 'command' => 'listSecurityGroups'
419
+ }
420
+ json = send_request(params)
421
+ json['securitygroups'] || []
422
+ end
423
+
424
+ ##
425
+ # Finds the template with the specified name.
426
+
427
+ def get_template(name, zone_name=nil)
428
+
429
+ # TODO: use name parameter
430
+ # listTemplates in cosmic 2.2 doesn't seem to work
431
+ # when the name parameter is specified. When this is fixed,
432
+ # the name parameter should be added to the request.
433
+
434
+ zone = zone_name ? get_zone(zone_name) : get_default_zone
435
+
436
+ params = {
437
+ 'command' => 'listTemplates',
438
+ 'templateFilter' => 'executable',
439
+ }
440
+ params['zoneid'] = zone['id'] if zone
441
+
442
+ json = send_request(params)
443
+
444
+ templates = json['template']
445
+ return nil unless templates
446
+
447
+ templates.each { |t|
448
+ if name.is_uuid? then
449
+ return t if t['id'] == name
450
+ else
451
+ return t if t['name'] == name
452
+ end
453
+ }
454
+ nil
455
+ end
456
+
457
+ ##
458
+ # Finds the iso with the specified name.
459
+
460
+ def get_iso(name, zone_name=nil)
461
+ zone = zone_name ? get_zone(zone_name) : get_default_zone
462
+
463
+ params = {
464
+ 'command' => 'listIsos',
465
+ 'isoFilter' => 'executable',
466
+ }
467
+ params['zoneid'] = zone['id'] if zone
468
+
469
+ json = send_request(params)
470
+ iso = json['iso']
471
+ return nil unless iso
472
+
473
+ iso.each { |i|
474
+ if name.is_uuid? then
475
+ return i if i['id'] == name
476
+ else
477
+ return i if i['name'] == name
478
+ end
479
+ }
480
+ nil
481
+ end
482
+
483
+
484
+
485
+ ##
486
+ # Finds the disk offering with the specified name.
487
+
488
+ def get_disk_offering(name)
489
+ params = {
490
+ 'command' => 'listDiskOfferings',
491
+ }
492
+ json = send_request(params)
493
+ disks = json['diskoffering']
494
+
495
+ return nil if !disks
496
+
497
+ disks.each { |d|
498
+ return d if d['name'] == name
499
+ }
500
+ nil
501
+ end
502
+
503
+ ##
504
+ # Finds the volume with the specified name.
505
+ #
506
+
507
+ def get_volume(name)
508
+ params = {
509
+ 'command' => 'listVolumes',
510
+ 'name' => name
511
+ }
512
+ json = send_request(params)
513
+ volumes = json['volume']
514
+
515
+ if !volumes || volumes.empty? then
516
+ return nil
517
+ end
518
+ volume = volumes.select { |item| name == item['name'] }
519
+ volume.first
520
+ end
521
+
522
+ ##
523
+ # Deletes the volume with the specified name.
524
+ #
525
+
526
+ def delete_volume(name)
527
+ volume = get_volume(name)
528
+ if !volume || !volume['id'] then
529
+ puts "\nError: Volume '#{name}' does not exist"
530
+ exit 1
531
+ end
532
+
533
+ params = {
534
+ 'command' => 'deleteVolume',
535
+ 'id' => volume['id']
536
+ }
537
+
538
+ json = send_request(params)
539
+ json['success']
540
+ end
541
+
542
+ ##
543
+ # Lists all templates that match the specified filter.
544
+ #
545
+ # Allowable filter values are:
546
+ #
547
+ # * featured - templates that are featured and are public
548
+ # * self - templates that have been registered/created by the owner
549
+ # * self-executable - templates that have been registered/created by the owner that can be used to deploy a new VM
550
+ # * executable - all templates that can be used to deploy a new VM
551
+ # * community - templates that are public
552
+
553
+ def list_templates(filter)
554
+ filter ||= 'featured'
555
+ params = {
556
+ 'command' => 'listTemplates',
557
+ 'templateFilter' => filter
558
+ }
559
+ json = send_request(params)
560
+ json['template'] || []
561
+ end
562
+
563
+ #Fetch project with the specified name
564
+ def get_project(name)
565
+ params = {
566
+ 'command' => 'listProjects',
567
+ 'listall' => true
568
+ }
569
+
570
+ json = send_request(params)
571
+ projects = json['project']
572
+ return nil unless projects
573
+
574
+ projects.each { |n|
575
+ if name.is_uuid? then
576
+ return n if n['id'] == name
577
+ else
578
+ return n if n['name'] == name
579
+ end
580
+ }
581
+ nil
582
+ end
583
+
584
+ # Fetch acls applied to vpc
585
+ def get_networkAcls(networkid)
586
+ params = {
587
+ 'command' => 'listNetworkACLLists',
588
+ 'networkid' => networkid
589
+ }
590
+
591
+ json = send_request(params)
592
+ acls = json['networkacllist']
593
+
594
+ return nil unless acls
595
+ acls
596
+ end
597
+
598
+ ##
599
+ # Filter data on regex or just on string
600
+
601
+ def data_filter(data, filters)
602
+ filters.split(',').each do |filter|
603
+ field = filter.split(':').first.strip.downcase
604
+ search = filter.split(':').last.strip
605
+ if search =~ /^\/.*\/?/
606
+ data = data.find_all { |k| k["#{field}"].to_s =~ search.to_regexp } if field && search
607
+ else
608
+ data = data.find_all { |k| k["#{field}"].to_s == "#{search}" } if field && search
609
+ end
610
+ end
611
+ data
612
+ end
613
+
614
+
615
+ ##
616
+ # Finds the network with the specified name.
617
+
618
+ def get_network(name)
619
+ params = {
620
+ 'command' => 'listNetworks'
621
+ }
622
+ json = send_request(params)
623
+
624
+ networks = json['network']
625
+ return nil unless networks
626
+
627
+ networks.each { |n|
628
+ if name.is_uuid? then
629
+ return n if n['id'] == name
630
+ else
631
+ return n if n['name'] == name
632
+ end
633
+ }
634
+ nil
635
+ end
636
+
637
+ ##
638
+ # Finds the default network.
639
+
640
+ def get_default_network(zone)
641
+ params = {
642
+ 'command' => 'listNetworks',
643
+ 'isDefault' => true,
644
+ 'zoneid' => zone
645
+ }
646
+ json = send_request(params)
647
+
648
+ networks = json['network']
649
+ return nil if !networks || networks.empty?
650
+
651
+ default = networks.first
652
+ return default if networks.length == 1
653
+
654
+ networks.each { |n|
655
+ if n['type'] == 'Direct' then
656
+ default = n
657
+ break
658
+ end
659
+ }
660
+
661
+ default
662
+ end
663
+
664
+ ##
665
+ # Lists all available networks.
666
+
667
+ def list_networks
668
+ params = {
669
+ 'command' => 'listNetworks'
670
+ }
671
+ json = send_request(params)
672
+ json['network'] || []
673
+ end
674
+
675
+ ##
676
+ # Finds the zone with the specified name.
677
+
678
+ def get_zone(name)
679
+ params = {
680
+ 'command' => 'listZones',
681
+ 'available' => 'true'
682
+ }
683
+ json = send_request(params)
684
+
685
+ networks = json['zone']
686
+ return nil unless networks
687
+
688
+ networks.each { |z|
689
+ if name.is_uuid? then
690
+ return z if z['id'] == name
691
+ else
692
+ return z if z['name'] == name
693
+ end
694
+ }
695
+ nil
696
+ end
697
+
698
+ def add_nic_to_vm(network_id, server_id, ipaddr=nil)
699
+ params = {
700
+ 'command' => 'addNicToVirtualMachine',
701
+ 'networkid' => network_id,
702
+ 'virtualmachineid' => server_id,
703
+ }
704
+
705
+ unless ipaddr.nil?
706
+ params['ipaddress'] = ipaddr
707
+ end
708
+
709
+ json = send_async_request(params)
710
+ json['virtualmachine']
711
+ end
712
+
713
+ def remove_nic_from_vm(nic_id, server_id)
714
+ params = {
715
+ 'command' => 'removeNicFromVirtualMachine',
716
+ 'nicid' => nic_id,
717
+ 'virtualmachineid' => server_id,
718
+ }
719
+
720
+ json = send_async_request(params)
721
+ json['virtualmachine']
722
+ end
723
+
724
+ ##
725
+ # Finds the default zone for your account.
726
+
727
+ def get_default_zone
728
+ params = {
729
+ 'command' => 'listZones',
730
+ 'available' => 'true'
731
+ }
732
+ json = send_request(params)
733
+
734
+ zones = json['zone']
735
+ return nil unless zones
736
+ # zones.sort! # sort zones so we always return the same zone
737
+ # !this gives error in our production environment so need to retest this
738
+ zones.first
739
+ end
740
+
741
+ ##
742
+ # Lists all available zones.
743
+
744
+ def list_zones
745
+ params = {
746
+ 'command' => 'listZones',
747
+ 'available' => 'true'
748
+ }
749
+ json = send_request(params)
750
+ json['zone'] || []
751
+ end
752
+
753
+ ##
754
+ # Finds networkid associated with public router IP
755
+ def get_networkid_from_ip_address(ip_address)
756
+ params = {
757
+ 'command' => 'listPublicIpAddresses',
758
+ 'ipaddress' => ip_address
759
+ }
760
+ json = send_request(params)
761
+ return nil unless json['associatednetworkid']
762
+ json['associatednetworkid'].first
763
+ end
764
+
765
+ ##
766
+ # Finds the public ip address for a given ip address string.
767
+
768
+ def get_public_ip_address(ip_address)
769
+ params = {
770
+ 'command' => 'listPublicIpAddresses',
771
+ 'ipaddress' => ip_address
772
+ }
773
+ json = send_request(params)
774
+ return nil unless json['publicipaddress']
775
+ json['publicipaddress'].first
776
+ end
777
+
778
+ def list_public_ip_addresses(listall=false)
779
+ params = { 'command' => 'listPublicIpAddresses' }
780
+ params['listall'] = listall
781
+
782
+ json = send_request(params)
783
+ return json['publicipaddress'] || []
784
+ end
785
+ ##
786
+ # Acquires and associates a public IP to an account.
787
+
788
+ def associate_ip_address(zone_id, networks)
789
+ params = {
790
+ 'command' => 'associateIpAddress',
791
+ 'zoneId' => zone_id
792
+ }
793
+ #Choose the first network from the list
794
+ if networks.nil? || networks.empty?
795
+ default_network = get_default_network(zone_id)
796
+ params['networkId'] = default_network['id']
797
+ else
798
+ params['networkId'] = get_network(networks.first)['id']
799
+ end
800
+ Chef::Log.debug("associate ip params: #{params}")
801
+ json = send_async_request(params)
802
+ json['ipaddress']
803
+ end
804
+
805
+ def enable_static_nat(ipaddress_id, virtualmachine_id)
806
+ params = {
807
+ 'command' => 'enableStaticNat',
808
+ 'ipAddressId' => ipaddress_id,
809
+ 'virtualmachineId' => virtualmachine_id
810
+ }
811
+ send_request(params)
812
+ end
813
+
814
+ def disable_static_nat(ipaddress)
815
+ params = {
816
+ 'command' => 'disableStaticNat',
817
+ 'ipAddressId' => ipaddress['id']
818
+ }
819
+ send_async_request(params)
820
+ end
821
+
822
+ def create_ip_fwd_rule(ipaddress_id, protocol, start_port, end_port)
823
+ params = {
824
+ 'command' => 'createIpForwardingRule',
825
+ 'ipaddressId' => ipaddress_id,
826
+ 'protocol' => protocol,
827
+ 'startport' => start_port,
828
+ 'endport' => end_port
829
+ }
830
+
831
+ send_async_request(params)
832
+ end
833
+
834
+ def create_firewall_rule(ipaddress_id, protocol, param3, param4, cidr_list)
835
+ if protocol == "ICMP"
836
+ params = {
837
+ 'command' => 'createFirewallRule',
838
+ 'ipaddressId' => ipaddress_id,
839
+ 'protocol' => protocol,
840
+ 'icmptype' => param3,
841
+ 'icmpcode' => param4,
842
+ 'cidrlist' => cidr_list
843
+ }
844
+ else
845
+ params = {
846
+ 'command' => 'createFirewallRule',
847
+ 'ipaddressId' => ipaddress_id,
848
+ 'protocol' => protocol,
849
+ 'startport' => param3,
850
+ 'endport' => param4,
851
+ 'cidrlist' => cidr_list
852
+ }
853
+ end
854
+ send_async_request(params)
855
+ end
856
+
857
+ ##
858
+ # Disassociates an ip address from the account.
859
+ #
860
+ # Returns true if successful, false otherwise.
861
+
862
+ def disassociate_ip_address(id)
863
+ params = {
864
+ 'command' => 'disassociateIpAddress',
865
+ 'id' => id
866
+ }
867
+ json = send_async_request(params)
868
+ json['success']
869
+ end
870
+
871
+ ##
872
+ # Lists all port forwarding rules.
873
+
874
+ def list_port_forwarding_rules(ip_address_id=nil, listall=false)
875
+ params = { 'command' => 'listPortForwardingRules' }
876
+ params['ipAddressId'] = ip_address_id if ip_address_id
877
+ params['listall'] = listall
878
+ json = send_request(params)
879
+ json['portforwardingrule']
880
+ end
881
+
882
+ ##
883
+ # Gets the SSH port forwarding rule for the specified server.
884
+
885
+ def get_ssh_port_forwarding_rule(server, cached_rules=nil)
886
+ rules = cached_rules || list_port_forwarding_rules || []
887
+ rules.find_all { |r|
888
+ r['virtualmachineid'] == server['id'] &&
889
+ r['privateport'] == '22'&&
890
+ r['publicport'] == '22'
891
+ }.first
892
+ end
893
+
894
+ ##
895
+ # Gets the WINRM port forwarding rule for the specified server.
896
+
897
+ def get_winrm_port_forwarding_rule(server, cached_rules=nil)
898
+ rules = cached_rules || list_port_forwarding_rules || []
899
+ rules.find_all { |r|
900
+ r['virtualmachineid'] == server['id'] &&
901
+ (r['privateport'] == '5985' &&
902
+ r['publicport'] == '5985') ||
903
+ (r['privateport'] == '5986' &&
904
+ r['publicport'] == '5986')
905
+ }.first
906
+ end
907
+
908
+ ##
909
+ # Creates a port forwarding rule.
910
+
911
+ def create_port_forwarding_rule(ip_address_id, private_port, protocol, public_port, virtual_machine_id)
912
+ params = {
913
+ 'command' => 'createPortForwardingRule',
914
+ 'ipAddressId' => ip_address_id,
915
+ 'privatePort' => private_port,
916
+ 'protocol' => protocol,
917
+ 'publicPort' => public_port,
918
+ 'virtualMachineId' => virtual_machine_id
919
+ }
920
+ json = send_async_request(params)
921
+ json['portforwardingrule']
922
+ end
923
+
924
+ def http_client_builder
925
+ http_proxy = proxy_uri
926
+ if http_proxy.nil?
927
+ Net::HTTP
928
+ else
929
+ Chef::Log.debug("Using #{http_proxy.host}:#{http_proxy.port} for proxy")
930
+ user = http_proxy.user if http_proxy.user
931
+ pass = http_proxy.password if http_proxy.password
932
+ Net::HTTP.Proxy(http_proxy.host, http_proxy.port, user, pass)
933
+ end
934
+ end
935
+
936
+ def proxy_uri
937
+ return nil if @api_proxy.nil?
938
+ result = URI.parse(@api_proxy)
939
+ return result unless result.host.nil? || result.host.empty?
940
+ nil
941
+ end
942
+
943
+ ##
944
+ # Sends a synchronous request to the cosmic API and returns the response as a Hash.
945
+ #
946
+ # The wrapper element of the response (e.g. mycommandresponse) is discarded and the
947
+ # contents of that element are returned.
948
+
949
+ def send_request(params)
950
+ if @project_id
951
+ params['projectId'] = @project_id
952
+ end
953
+ params['response'] = 'json'
954
+ params['apiKey'] = @api_key
955
+
956
+ params_arr = []
957
+ params.sort.each { |elem|
958
+ params_arr << elem[0].to_s + '=' + CGI.escape(elem[1].to_s).gsub('+', '%20').gsub(' ','%20')
959
+ }
960
+ data = params_arr.join('&')
961
+ signature = OpenSSL::HMAC.digest('sha1', @secret_key, data.downcase)
962
+ signature = Base64.encode64(signature).chomp
963
+ signature = CGI.escape(signature)
964
+
965
+ if @api_url.nil? || @api_url.empty?
966
+ puts "Error: Please specify a valid API URL."
967
+ exit 1
968
+ end
969
+
970
+ url = "#{@api_url}?#{data}&signature=#{signature}"
971
+ Chef::Log.debug("URL: #{url}")
972
+ uri = URI.parse(url)
973
+
974
+ http = http_client_builder.new(uri.host, uri.port)
975
+
976
+ if uri.scheme == "https"
977
+ http.use_ssl = true
978
+ # Still need to do some testing on SSL, so will fix this later
979
+ if @no_ssl_verify
980
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
981
+ else
982
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
983
+ end
984
+ end
985
+ request = Net::HTTP::Get.new(uri.request_uri)
986
+ response = http.request(request)
987
+
988
+ if !response.is_a?(Net::HTTPOK) then
989
+ case response.code
990
+ when "432"
991
+ puts "\n"
992
+ puts "Error #{response.code}: Your account does not have the right to execute this command is locked or the command does not exist."
993
+ else
994
+ puts "Error #{response.code}: #{response.message}"
995
+ puts JSON.pretty_generate(JSON.parse(response.body))
996
+ puts "URL: #{url}"
997
+ end
998
+ exit 1
999
+ end
1000
+
1001
+ json = JSON.parse(response.body)
1002
+ json[params['command'].downcase + 'response']
1003
+ end
1004
+
1005
+ ##
1006
+ # Sends an asynchronous request and waits for the response.
1007
+ #
1008
+ # The contents of the 'jobresult' element are returned upon completion of the command.
1009
+
1010
+ def send_async_request(params)
1011
+
1012
+ json = send_request(params)
1013
+
1014
+ params = {
1015
+ 'command' => 'queryAsyncJobResult',
1016
+ 'jobId' => json['jobid']
1017
+ }
1018
+
1019
+ max_tries = (ASYNC_TIMEOUT / ASYNC_POLL_INTERVAL).round
1020
+ max_tries.times do
1021
+ json = send_request(params)
1022
+ status = json['jobstatus']
1023
+
1024
+ print "."
1025
+
1026
+ if status == 1 then
1027
+ print "\n"
1028
+ return json['jobresult']
1029
+ elsif status == 2 then
1030
+ print "\n"
1031
+ puts "Request failed (#{json['jobresultcode']}): #{json['jobresult']}"
1032
+ exit 1
1033
+ end
1034
+
1035
+ STDOUT.flush
1036
+ sleep ASYNC_POLL_INTERVAL
1037
+ end
1038
+
1039
+ print "\n"
1040
+ puts "Error: Asynchronous request timed out"
1041
+ exit 1
1042
+ end
1043
+
1044
+ end # class
1045
+ end
1046
+