knife-cloudstack 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
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