knife-cloudstack 0.0.13 → 0.0.14

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 (38) hide show
  1. data/CHANGES.rdoc +50 -0
  2. data/README.rdoc +221 -42
  3. data/lib/chef/knife/cs_account_list.rb +130 -0
  4. data/lib/chef/knife/cs_base.rb +98 -0
  5. data/lib/chef/knife/cs_baselist.rb +81 -0
  6. data/lib/chef/knife/cs_cluster_list.rb +93 -0
  7. data/lib/chef/knife/cs_config_list.rb +85 -0
  8. data/lib/chef/knife/cs_disk_list.rb +89 -0
  9. data/lib/chef/knife/cs_domain_list.rb +83 -0
  10. data/lib/chef/knife/cs_firewallrule_list.rb +95 -0
  11. data/lib/chef/knife/cs_host_list.rb +95 -0
  12. data/lib/chef/knife/cs_hosts.rb +2 -2
  13. data/lib/chef/knife/cs_iso_list.rb +103 -0
  14. data/lib/chef/knife/cs_network_list.rb +56 -46
  15. data/lib/chef/knife/cs_oscategory_list.rb +78 -0
  16. data/lib/chef/knife/cs_ostype_list.rb +80 -0
  17. data/lib/chef/knife/cs_pod_list.rb +93 -0
  18. data/lib/chef/knife/cs_project_list.rb +92 -0
  19. data/lib/chef/knife/cs_router_list.rb +94 -0
  20. data/lib/chef/knife/cs_server_create.rb +185 -144
  21. data/lib/chef/knife/cs_server_delete.rb +62 -79
  22. data/lib/chef/knife/cs_server_list.rb +136 -57
  23. data/lib/chef/knife/cs_server_reboot.rb +50 -54
  24. data/lib/chef/knife/cs_server_start.rb +48 -52
  25. data/lib/chef/knife/cs_server_stop.rb +54 -55
  26. data/lib/chef/knife/cs_service_list.rb +62 -41
  27. data/lib/chef/knife/cs_stack_create.rb +2 -2
  28. data/lib/chef/knife/cs_stack_delete.rb +2 -2
  29. data/lib/chef/knife/cs_template_create.rb +121 -0
  30. data/lib/chef/knife/cs_template_extract.rb +104 -0
  31. data/lib/chef/knife/cs_template_list.rb +65 -63
  32. data/lib/chef/knife/cs_template_register.rb +180 -0
  33. data/lib/chef/knife/cs_user_list.rb +93 -0
  34. data/lib/chef/knife/cs_volume_list.rb +94 -0
  35. data/lib/chef/knife/cs_zone_list.rb +55 -36
  36. data/lib/knife-cloudstack/connection.rb +75 -22
  37. data/lib/knife-cloudstack/string_to_regexp.rb +32 -0
  38. metadata +93 -61
@@ -0,0 +1,93 @@
1
+ #
2
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
+ # Copyright:: Copyright (c) 2013 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife/cs_base'
20
+ require 'chef/knife/cs_baselist'
21
+
22
+ module KnifeCloudstack
23
+ class CsUserList < Chef::Knife
24
+
25
+ include Chef::Knife::KnifeCloudstackBase
26
+ include Chef::Knife::KnifeCloudstackBaseList
27
+
28
+ deps do
29
+ require 'knife-cloudstack/connection'
30
+ Chef::Knife.load_deps
31
+ end
32
+
33
+ banner "knife cs user list (options)"
34
+
35
+ option :listall,
36
+ :long => "--listall",
37
+ :description => "List all users",
38
+ :boolean => true
39
+
40
+ option :keyword,
41
+ :long => "--keyword KEY",
42
+ :description => "List by keyword"
43
+
44
+ def run
45
+ validate_base_options
46
+
47
+ if locate_config_value(:fields)
48
+ object_list = []
49
+ locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
50
+ else
51
+ object_list = [
52
+ ui.color('Account', :bold),
53
+ ui.color('Type', :bold),
54
+ ui.color('State', :bold),
55
+ ui.color('Domain', :bold),
56
+ ui.color('Username', :bold),
57
+ ui.color('First', :bold),
58
+ ui.color('Last', :bold)
59
+ ]
60
+ end
61
+
62
+ columns = object_list.count
63
+ object_list = [] if locate_config_value(:noheader)
64
+
65
+ connection_result = connection.list_object(
66
+ "listUsers",
67
+ "user",
68
+ locate_config_value(:filter),
69
+ locate_config_value(:listall),
70
+ locate_config_value(:keyword)
71
+ )
72
+
73
+ output_format(connection_result)
74
+
75
+ connection_result.each do |r|
76
+ if locate_config_value(:fields)
77
+ locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
78
+ else
79
+ object_list << r['account'].to_s
80
+ object_list << r['accounttype'].to_s
81
+ object_list << r['state'].to_s
82
+ object_list << r['domain'].to_s
83
+ object_list << r['username'].to_s
84
+ object_list << r['firstname'].to_s
85
+ object_list << r['lastname'].to_s
86
+ end
87
+ end
88
+ puts ui.list(object_list, :uneven_columns_across, columns)
89
+ list_object_fields(connection_result) if locate_config_value(:fieldlist)
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,94 @@
1
+ #
2
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
+ # Copyright:: Copyright (c) 2013 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife/cs_base'
20
+ require 'chef/knife/cs_baselist'
21
+
22
+ module KnifeCloudstack
23
+ class CsVolumeList < Chef::Knife
24
+
25
+ include Chef::Knife::KnifeCloudstackBase
26
+ include Chef::Knife::KnifeCloudstackBaseList
27
+
28
+ deps do
29
+ require 'knife-cloudstack/connection'
30
+ Chef::Knife.load_deps
31
+ end
32
+
33
+ banner "knife cs volume list (options)"
34
+
35
+ option :listall,
36
+ :long => "--listall",
37
+ :description => "List all volumes",
38
+ :boolean => true
39
+
40
+ option :name,
41
+ :long => "--name NAME",
42
+ :description => "Specify volume name to list"
43
+
44
+ option :keyword,
45
+ :long => "--keyword KEY",
46
+ :description => "List by keyword"
47
+
48
+ def run
49
+ validate_base_options
50
+
51
+ object_list = []
52
+ if locate_config_value(:fields)
53
+ locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
54
+ else
55
+ object_list << ui.color('Name', :bold)
56
+ object_list << ui.color('Account', :bold) unless locate_config_value(:cloudstack_project)
57
+ object_list << ui.color('Domain', :bold)
58
+ object_list << ui.color('State', :bold)
59
+ object_list << ui.color('VMName', :bold)
60
+ object_list << ui.color('VMState', :bold)
61
+ end
62
+
63
+ columns = object_list.count
64
+ object_list = [] if locate_config_value(:noheader)
65
+
66
+ connection_result = connection.list_object(
67
+ "listVolumes",
68
+ "volume",
69
+ locate_config_value(:filter),
70
+ locate_config_value(:listall),
71
+ locate_config_value(:keyword),
72
+ locate_config_value(:name)
73
+ )
74
+
75
+ output_format(connection_result)
76
+
77
+ connection_result.each do |r|
78
+ if locate_config_value(:fields)
79
+ locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
80
+ else
81
+ object_list << r['name'].to_s
82
+ object_list << r['account'].to_s unless locate_config_value(:cloudstack_project)
83
+ object_list << r['domain'].to_s
84
+ object_list << r['state'].to_s
85
+ object_list << r['vmname'].to_s
86
+ object_list << r['vmstate'].to_s
87
+ end
88
+ end
89
+ puts ui.list(object_list, :uneven_columns_across, columns)
90
+ list_object_fields(connection_result) if locate_config_value(:fieldlist)
91
+ end
92
+
93
+ end
94
+ end
@@ -1,6 +1,8 @@
1
1
  #
2
2
  # Author:: Ryan Holmes (<rholmes@edmunds.com>)
3
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
3
4
  # Copyright:: Copyright (c) 2011 Edmunds, Inc.
5
+ # Copyright:: Copyright (c) 2013 Sander Botman.
4
6
  # License:: Apache License, Version 2.0
5
7
  #
6
8
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,62 +18,79 @@
16
18
  # limitations under the License.
17
19
  #
18
20
 
19
- require 'chef/knife'
21
+ require 'chef/knife/cs_base'
22
+ require 'chef/knife/cs_baselist'
20
23
 
21
24
  module KnifeCloudstack
22
25
  class CsZoneList < Chef::Knife
23
26
 
27
+ include Chef::Knife::KnifeCloudstackBase
28
+ include Chef::Knife::KnifeCloudstackBaseList
29
+
24
30
  deps do
25
31
  require 'knife-cloudstack/connection'
32
+ require 'chef/knife'
33
+ Chef::Knife.load_deps
26
34
  end
27
35
 
28
36
  banner "knife cs zone list (options)"
29
37
 
30
- option :cloudstack_url,
31
- :short => "-U URL",
32
- :long => "--cloudstack-url URL",
33
- :description => "The CloudStack endpoint URL",
34
- :proc => Proc.new { |url| Chef::Config[:knife][:cloudstack_url] = url }
35
-
36
- option :cloudstack_api_key,
37
- :short => "-A KEY",
38
- :long => "--cloudstack-api-key KEY",
39
- :description => "Your CloudStack API key",
40
- :proc => Proc.new { |key| Chef::Config[:knife][:cloudstack_api_key] = key }
38
+ option :keyword,
39
+ :long => "--keyword KEY",
40
+ :description => "List by keyword"
41
41
 
42
- option :cloudstack_secret_key,
43
- :short => "-K SECRET",
44
- :long => "--cloudstack-secret-key SECRET",
45
- :description => "Your CloudStack secret key",
46
- :proc => Proc.new { |key| Chef::Config[:knife][:cloudstack_secret_key] = key }
42
+ option :index,
43
+ :long => "--index",
44
+ :description => "Add index numbers to the output",
45
+ :boolean => true
47
46
 
48
47
  def run
48
+ validate_base_options
49
49
 
50
- connection = CloudstackClient::Connection.new(
51
- locate_config_value(:cloudstack_url),
52
- locate_config_value(:cloudstack_api_key),
53
- locate_config_value(:cloudstack_secret_key)
54
- )
50
+ object_list = []
51
+ object_list << ui.color('Index', :bold) if locate_config_value(:index)
55
52
 
56
- zone_list = [
57
- ui.color('Name', :bold),
53
+ if locate_config_value(:fields)
54
+ object_list = []
55
+ locate_config_value(:fields).split(',').each { |n| object_list << ui.color(("#{n}").strip, :bold) }
56
+ else
57
+ [
58
+ ui.color('Name', :bold),
58
59
  ui.color('Network Type', :bold),
59
60
  ui.color('Security Groups', :bold)
60
- ]
61
-
62
- zones = connection.list_zones
63
- zones.each do |z|
64
- zone_list << z['name']
65
- zone_list << z['networktype']
66
- zone_list << z['securitygroupsenabled'].to_s
61
+ ].each { |field| object_list << field }
67
62
  end
68
- puts ui.list(zone_list, :columns_across, 3)
69
63
 
70
- end
64
+ columns = object_list.count
65
+ object_list = [] if locate_config_value(:noheader)
71
66
 
72
- def locate_config_value(key)
73
- key = key.to_sym
74
- Chef::Config[:knife][key] || config[key]
67
+ connection_result = connection.list_object(
68
+ "listZones",
69
+ "zone",
70
+ locate_config_value(:filter),
71
+ false,
72
+ locate_config_value(:keyword)
73
+ )
74
+
75
+ output_format(connection_result)
76
+
77
+ index_num = 0
78
+ connection_result.each do |r|
79
+ if locate_config_value(:index)
80
+ index_num += 1
81
+ object_list << index_num.to_s
82
+ end
83
+
84
+ if locate_config_value(:fields)
85
+ locate_config_value(:fields).downcase.split(',').each { |n| object_list << ((r[("#{n}").strip]).to_s || 'N/A') }
86
+ else
87
+ object_list << r['name'].to_s
88
+ object_list << r['networktype'].to_s
89
+ object_list << r['securitygroupsenabled'].to_s
90
+ end
91
+ end
92
+ puts ui.list(object_list, :uneven_columns_across, columns)
93
+ list_object_fields(connection_result) if locate_config_value(:fieldlist)
75
94
  end
76
95
 
77
96
  end
@@ -1,6 +1,8 @@
1
1
  #
2
2
  # Author:: Ryan Holmes (<rholmes@edmunds.com>)
3
3
  # Author:: KC Braunschweig (<kcbraunschweig@gmail.com>)
4
+ # Author:: Sander Botman (<sbotman@schubergphilis.com>)
5
+ # Author:: Frank Breedijk (<fbreedijk@schubergphilis.com>)
4
6
  # Copyright:: Copyright (c) 2011 Edmunds, Inc.
5
7
  # License:: Apache License, Version 2.0
6
8
  #
@@ -24,6 +26,8 @@ require 'uri'
24
26
  require 'cgi'
25
27
  require 'net/http'
26
28
  require 'json'
29
+ require 'highline/import'
30
+ require 'knife-cloudstack/string_to_regexp'
27
31
 
28
32
  module CloudstackClient
29
33
  class Connection
@@ -85,7 +89,7 @@ module CloudstackClient
85
89
  if ip_addr
86
90
  return ip_addr['ipaddress']
87
91
  end
88
- nic['ipaddress']
92
+ nic['ipaddress'] || []
89
93
  end
90
94
 
91
95
  ##
@@ -112,6 +116,31 @@ module CloudstackClient
112
116
  end
113
117
  end
114
118
 
119
+ ##
120
+ # List all the objects based on the command that is specified.
121
+
122
+ def list_object(command, json_result, filter=nil, listall=nil, keyword=nil, name=nil, templatefilter=nil)
123
+ params = {
124
+ 'command' => command
125
+ }
126
+ params['listall'] = true if listall || name || keyword unless listall == false
127
+ params['keyword'] = keyword if keyword
128
+ params['name'] = name if name
129
+
130
+ if templatefilter
131
+ template = 'featured'
132
+ template = templatefilter.downcase if ["featured","self","self-executable","executable","community"].include?(templatefilter.downcase)
133
+ params['templateFilter'] = template
134
+ end
135
+
136
+ json = send_request(params)
137
+ Chef::Log.debug("JSON (list_object) result: #{json}")
138
+
139
+ result = json["#{json_result}"] || []
140
+ result = data_filter(result, filter) if filter
141
+ result
142
+ end
143
+
115
144
  ##
116
145
  # Lists all the servers in your account.
117
146
 
@@ -119,9 +148,6 @@ module CloudstackClient
119
148
  params = {
120
149
  'command' => 'listVirtualMachines'
121
150
  }
122
- # if @project_id
123
- # params['projectId'] = @project_id
124
- # end
125
151
  json = send_request(params)
126
152
  json['virtualmachine'] || []
127
153
  end
@@ -129,7 +155,7 @@ module CloudstackClient
129
155
  ##
130
156
  # Deploys a new server using the specified parameters.
131
157
 
132
- def create_server(host_name, service_name, template_name, zone_name=nil, network_names=[])
158
+ def create_server(host_name, service_name, template_name, zone_name=nil, network_names=[], extra_params)
133
159
 
134
160
  if host_name then
135
161
  if get_server(host_name) then
@@ -184,9 +210,8 @@ module CloudstackClient
184
210
  'zoneId' => zone['id'],
185
211
  'networkids' => network_ids.join(',')
186
212
  }
187
- # if @project_id
188
- # params['projectId'] = @project_id
189
- # end
213
+
214
+ params.merge!(extra_params) if extra_params
190
215
 
191
216
  params['name'] = host_name if host_name
192
217
 
@@ -365,7 +390,8 @@ module CloudstackClient
365
390
  #Fetch project with the specified name
366
391
  def get_project(name)
367
392
  params = {
368
- 'command' => 'listProjects'
393
+ 'command' => 'listProjects',
394
+ 'listall' => true
369
395
  }
370
396
 
371
397
  json = send_request(params)
@@ -380,6 +406,22 @@ module CloudstackClient
380
406
  nil
381
407
  end
382
408
 
409
+ ##
410
+ # Filter data on regex or just on string
411
+
412
+ def data_filter(data, filters)
413
+ filters.split(',').each do |filter|
414
+ field = filter.split(':').first.strip.downcase
415
+ search = filter.split(':').last.strip
416
+ if search =~ /^\/.*\/?/
417
+ data = data.find_all { |k| k["#{field}"].to_s =~ search.to_regexp } if field && search
418
+ else
419
+ data = data.find_all { |k| k["#{field}"].to_s == "#{search}" } if field && search
420
+ end
421
+ end
422
+ data
423
+ end
424
+
383
425
 
384
426
  ##
385
427
  # Finds the network with the specified name.
@@ -388,9 +430,6 @@ module CloudstackClient
388
430
  params = {
389
431
  'command' => 'listNetworks'
390
432
  }
391
- # if @project_id
392
- # params['projectId'] = @project_id
393
- # end
394
433
  json = send_request(params)
395
434
 
396
435
  networks = json['network']
@@ -414,9 +453,6 @@ module CloudstackClient
414
453
  'isDefault' => true,
415
454
  'zoneid' => zone
416
455
  }
417
- # if @project_id
418
- # params['projectId'] = @project_id
419
- # end
420
456
  json = send_request(params)
421
457
 
422
458
  networks = json['network']
@@ -513,7 +549,7 @@ module CloudstackClient
513
549
  params = { 'command' => 'listPublicIpAddresses'}
514
550
 
515
551
  json = send_request(params)
516
- return json['publicipaddress']
552
+ return json['publicipaddress'] || []
517
553
  end
518
554
  ##
519
555
  # Acquires and associates a public IP to an account.
@@ -564,6 +600,17 @@ module CloudstackClient
564
600
  send_async_request(params)
565
601
  end
566
602
 
603
+ def create_firewall_rule(ipaddress_id, protocol, start_port, end_port, cidr_list)
604
+ params = {
605
+ 'command' => 'createFirewallRule',
606
+ 'ipaddressId' => ipaddress_id,
607
+ 'protocol' => protocol,
608
+ 'startport' => start_port,
609
+ 'endport' => end_port,
610
+ 'cidrlist' => cidr_list
611
+ }
612
+ send_async_request(params)
613
+ end
567
614
 
568
615
  ##
569
616
  # Disassociates an ip address from the account.
@@ -634,15 +681,15 @@ module CloudstackClient
634
681
 
635
682
  params_arr = []
636
683
  params.sort.each { |elem|
637
- params_arr << elem[0].to_s + '=' + elem[1].to_s
684
+ params_arr << elem[0].to_s + '=' + CGI.escape(elem[1].to_s).gsub('+', '%20').gsub(' ','%20')
638
685
  }
639
686
  data = params_arr.join('&')
640
- encoded_data = URI.encode(data.downcase).gsub('+', '%20').gsub(',', '%2c')
641
- signature = OpenSSL::HMAC.digest('sha1', @secret_key, encoded_data)
687
+ signature = OpenSSL::HMAC.digest('sha1', @secret_key, data.downcase)
642
688
  signature = Base64.encode64(signature).chomp
643
689
  signature = CGI.escape(signature)
644
690
 
645
691
  url = "#{@api_url}?#{data}&signature=#{signature}"
692
+ Chef::Log.debug("URL: #{url}")
646
693
  uri = URI.parse(url)
647
694
  http = Net::HTTP.new(uri.host, uri.port)
648
695
  http.use_ssl = @use_ssl
@@ -651,9 +698,15 @@ module CloudstackClient
651
698
  response = http.request(request)
652
699
 
653
700
  if !response.is_a?(Net::HTTPOK) then
654
- puts "Error #{response.code}: #{response.message}"
655
- puts JSON.pretty_generate(JSON.parse(response.body))
656
- puts "URL: #{url}"
701
+ case response.code
702
+ when "432"
703
+ puts "\n"
704
+ puts "Error #{response.code}: Your account does not have the right to execute this command or the command does not exist."
705
+ else
706
+ puts "Error #{response.code}: #{response.message}"
707
+ puts JSON.pretty_generate(JSON.parse(response.body))
708
+ puts "URL: #{url}"
709
+ end
657
710
  exit 1
658
711
  end
659
712