cloudstack-cli 1.0.0.rc3 → 1.0.0.rc4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85939f310bb8ee9429a065100d5bfa4704a6d016
4
- data.tar.gz: b86324e4d2083680e848e765cb0df50cd9833c0d
3
+ metadata.gz: c1fddcccc9c2fe8010aeb200b88b033aa1d97d1e
4
+ data.tar.gz: 6e7b9cc1af09643f543266cef7c96c1aec3b9cea
5
5
  SHA512:
6
- metadata.gz: a469af847c11b94a289f1e0a90012e0d395433c62298ce640ccdb3cbe897dd457568d19f499b1f14486987be13714636b783f9e86fd515febac05b2b888e3689
7
- data.tar.gz: 2225637aad656507ced2b8e1f511f3ffd551489512599b70f0b558bd73fe40c4007a5bc29fa6727c7415f37ccf06e9c4e60dee28ae371e7682abb40a2ea0d1f7
6
+ metadata.gz: 0fb2398fdda615a61506c7ab7eb1e5974538f4a9bf8dd160ff18263ef133aa82a46c797e629157bb40d5a900d0a92b94d698a98bb429389a5d67efc580bf7223
7
+ data.tar.gz: 54ef72715d16d1042a0a5c78852c8f99fe8e678111d64eceb27cb4a06b49f98ffdd7452ca8896bb55392bbd30389596f4b7fea469eb829a8fa2b8f58072a234c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloudstack-cli (1.0.0.rc3)
4
+ cloudstack-cli (1.0.0.rc4)
5
5
  cloudstack_client (~> 1.0.3)
6
6
  thor (~> 0.19.1)
7
7
 
data/README.md CHANGED
@@ -149,14 +149,8 @@ Stop all virtual routers of project named Demo (you could filter by zone too):
149
149
  $ cloudstack-cli router list --project Demo --status running --redundant-state BACKUP --command STOP
150
150
  ````
151
151
 
152
- **Hint:** You can watch the status of the command with watch.
153
-
154
- ```bash
155
- $ watch -n cloudstack-cli router list --project Demo
156
- ```
157
-
158
-
159
152
  ## References
153
+
160
154
  - [Cloudstack API documentation](http://cloudstack.apache.org/docs/api/)
161
155
  - This tool was inspired by the Knife extension for Cloudstack: [knife-cloudstack](https://github.com/CloudStack-extras/knife-cloudstack)
162
156
 
@@ -78,7 +78,7 @@ module CloudstackCli
78
78
  end
79
79
 
80
80
  def filter_by(objects, key, value)
81
- objects.select {|r| r[key].downcase == value.downcase}
81
+ objects.select {|r| r[key.to_s].downcase == value.downcase}
82
82
  end
83
83
 
84
84
  def parse_file(file, extensions = %w(.json .yaml .yml))
@@ -15,4 +15,61 @@ class Domain < CloudstackCli::Base
15
15
  end
16
16
  end
17
17
 
18
+ desc 'create', 'create domain'
19
+ option :network_domain, desc: "Network domain for networks in the domain."
20
+ option :parent_domain, desc: "Assigns new domain a parent domain by domain name of the parent. If no parent domain is specied, the ROOT domain is assumed."
21
+ def create(name)
22
+ create_domains([options.merge(name: name)])
23
+ end
24
+
25
+ desc 'delete', 'delete domain'
26
+ option :parent_domain, desc: "Parent domain by domain name of the parent. If no parent domain is specied, the ROOT domain is assumed."
27
+ def delete(name)
28
+ delete_domains([options.merge(name: name)])
29
+ end
30
+
31
+ no_commands do
32
+
33
+ def create_domains(domains)
34
+ puts domains
35
+ domains.each do |domain|
36
+ say "Creating domain '#{domain['name']}'... "
37
+
38
+ if dom = client.list_domains(name: domain["name"], listall: true).first
39
+ unless domain["parent_domain"] && dom['parentdomainname'] != domain["parent_domain"]
40
+ say "domain '#{domain["name"]}' already exists.", :yellow
41
+ next
42
+ end
43
+ end
44
+
45
+ if domain["parent_domain"]
46
+ parent = client.list_domains(name: domain["parent_domain"], listall: true).first
47
+ unless parent
48
+ say "parent domain '#{domain["parent_domain"]}' of domain '#{domain["name"]}' not found.", :yellow
49
+ next
50
+ end
51
+ domain['parentdomain_id'] = parent['id']
52
+ end
53
+
54
+ client.create_domain(domain) ? say("OK.", :green) : say("Failed.", :red)
55
+ end
56
+ end
57
+
58
+ def delete_domains(domains)
59
+ domains.each do |domain|
60
+ print "Deleting domain '#{domain['name']}'..."
61
+ if dom = client.list_domains(name: domain["name"], listall: true).first
62
+ if domain["parent_domain"] && dom['parentdomainname'] =! domain["parent_domain"]
63
+ say "domain '#{domain["name"]}' with same name found, but parent_domain '#{domain["parent_domain"]}' does not match.", :yellow
64
+ next
65
+ end
66
+ client.delete_domain(id: dom['id']) ? say(" OK.", :green) : say(" Failed.", :red)
67
+ else
68
+ say "domain '#{domain["name"]}' not found.", :yellow
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+
18
75
  end
@@ -2,26 +2,25 @@ class InfrastructureStack < CloudstackCli::Base
2
2
 
3
3
  desc "create STACKFILE", "create a infrastructure stack"
4
4
  def create(stackfile)
5
- puts stack = parse_file(stackfile)
5
+ stack = parse_file(stackfile)
6
6
  say "Create '#{stack["name"]}' infrastructure stack...", :green
7
7
 
8
- create_domains(stack['domains'])
8
+ Domain.new.create_domains(stack['domains'])
9
9
 
10
- say "Finished.", :green
10
+ say "Infrastructure stack '#{stack["name"]}' finished.", :green
11
11
  end
12
12
 
13
13
  desc "destroy STACKFILE", "destroy a infrastructure stack"
14
14
  def destroy(stackfile)
15
+ stack = parse_file(stackfile)
16
+ say "Destroy '#{stack["name"]}' infrastructure stack...", :green
15
17
 
16
- end
17
18
 
18
- no_commands do
19
- def create_domains(domains)
20
- domains.each do |domain|
21
- puts domain
22
- end
23
- end
24
19
 
20
+ Domain.new.delete_domains(stack['domains'].reverse)
21
+ say "Infrastructure stack '#{stack["name"]}' finished.", :green
25
22
  end
26
23
 
24
+
25
+
27
26
  end
@@ -4,7 +4,7 @@ class Router < CloudstackCli::Base
4
4
  option :project, desc: "name of the project"
5
5
  option :account, desc: "name of the account"
6
6
  option :zone, desc: "name of the zone"
7
- option :status, desc: "the status of the router"
7
+ option :state, desc: "the status of the router"
8
8
  option :redundant_state, desc: "the state of redundant virtual router",
9
9
  enum: %w(master backup fault unknown)
10
10
  option :listall, type: :boolean, desc: "list all routers", default: true
@@ -15,6 +15,8 @@ class Router < CloudstackCli::Base
15
15
  enum: %w(START STOP REBOOT)
16
16
  option :concurrency, type: :numeric, default: 10, aliases: '-C',
17
17
  desc: "number of concurrent command to execute"
18
+ option :format, default: "table",
19
+ enum: %w(table json yaml)
18
20
  def list
19
21
  resolve_project
20
22
  resolve_zone
@@ -23,8 +25,7 @@ class Router < CloudstackCli::Base
23
25
  routers = client.list_routers(options)
24
26
 
25
27
  if options[:listall]
26
- projects = client.list_projects
27
- projects.each do |project|
28
+ client.list_projects(listall: true).each do |project|
28
29
  routers = routers + client.list_routers(
29
30
  options.merge(projectid: project['id'])
30
31
  )
@@ -37,22 +38,24 @@ class Router < CloudstackCli::Base
37
38
 
38
39
  routers.reverse! if options[:reverse]
39
40
  print_routers(routers, options)
41
+ execute_router_commands(options[:command].downcase, routers) if options[:command]
42
+ end
40
43
 
41
- if options[:command]
42
- command = options[:command].downcase
43
- unless %w(start stop reboot).include?(command)
44
- say "\nCommand #{options[:command]} not supported.", :red
45
- exit 1
46
- end
47
- exit unless yes?("\n#{command.capitalize} the router(s) above? [y/N]:", :magenta)
48
- routers.each_slice(options[:concurrency]) do | batch |
49
- jobs = batch.map do |router|
50
- {id: client.send("#{command}_router", router['id'], async: false)['jobid'], name: "#{command.capitalize} router #{router['name']}"}
51
- end
52
- puts
53
- watch_jobs(jobs)
54
- end
55
- end
44
+ desc "list_from_file FILE", "list virtual routers from file"
45
+ option :reverse, type: :boolean, default: false, desc: "reverse listing of routers"
46
+ option :command,
47
+ desc: "command to execute for each router",
48
+ enum: %w(START STOP REBOOT)
49
+ option :concurrency, type: :numeric, default: 10, aliases: '-C',
50
+ desc: "number of concurrent command to execute"
51
+ option :format, default: "table",
52
+ enum: %w(table json yaml)
53
+ def list_from_file(file)
54
+ routers = parse_file(file)["routers"]
55
+
56
+ routers.reverse! if options[:reverse]
57
+ print_routers(routers, options)
58
+ execute_router_commands(options[:command].downcase, routers) if options[:command]
56
59
  end
57
60
 
58
61
  desc "stop NAME [NAME2 ..]", "stop virtual router(s)"
@@ -144,32 +147,58 @@ class Router < CloudstackCli::Base
144
147
  if routers.size < 1
145
148
  say "No routers found."
146
149
  else
147
- table = [[
148
- 'Name', 'Zone', 'Account', 'Project', 'Redundant-State', 'IP', 'Linklocal IP', 'Status', 'Redundant', 'OfferingID', 'Offering', 'ID'
149
- ]]
150
- table[0].delete('ID') unless options[:showid]
151
- routers.each do |router|
152
- table << [
153
- router["name"],
154
- router["zonename"],
155
- router["account"],
156
- router["project"],
157
- router["redundantstate"],
158
- router["nic"] && router["nic"].first ? router["nic"].first['ipaddress'] : "",
159
- router["linklocalip"],
160
- router["state"],
161
- router["isredundantrouter"],
162
- router["serviceofferingid"],
163
- router["serviceofferingname"],
164
- router["id"]
165
- ]
166
- table[-1].delete_at(-1) unless table[0].index "ID"
150
+ case options[:format].to_sym
151
+ when :yaml
152
+ puts({'routers' => routers}.to_yaml)
153
+ when :json
154
+ say JSON.pretty_generate(routers: routers)
155
+ else
156
+ table = [%w(
157
+ ID Name Zone Account Project Redundant-State IP Linklocal-IP Status Redundant Offering
158
+ )]
159
+ table[0].delete('ID') unless options[:showid]
160
+ routers.each do |router|
161
+ table << [
162
+ router["id"],
163
+ router["name"],
164
+ router["zonename"],
165
+ router["account"],
166
+ router["project"],
167
+ router["redundantstate"],
168
+ router["nic"] && router["nic"].first ? router["nic"].first['ipaddress'] : "",
169
+ router["linklocalip"],
170
+ router["state"],
171
+ router["isredundantrouter"],
172
+ router["serviceofferingname"]
173
+ ]
174
+ table[-1].delete_at(0) unless table[0].index "ID"
175
+ end
176
+ print_table table
177
+ puts
178
+ say "Total number of routers: #{routers.size}"
167
179
  end
168
- print_table table
169
- puts
170
- say "Total number of routers: #{routers.size}"
171
180
  end
172
181
  end
182
+
183
+ def execute_router_commands(command, routers)
184
+ unless %w(start stop reboot).include?(command)
185
+ say "\nCommand #{options[:command]} not supported.", :red
186
+ exit 1
187
+ end
188
+ exit unless yes?("\n#{command.capitalize} the router(s) above? [y/N]:", :magenta)
189
+
190
+ jobs = routers.map do |router|
191
+ {
192
+ job_id: nil,
193
+ object_id: router["id"],
194
+ name: "#{command.capitalize} router #{router['name']}",
195
+ status: -1
196
+ }
197
+ end
198
+
199
+ run_background_jobs(jobs, "#{command}_router")
200
+ end
201
+
173
202
  end
174
203
 
175
204
  end
@@ -1,10 +1,11 @@
1
1
  class Snapshot < CloudstackCli::Base
2
2
 
3
3
  desc 'list', 'list snapshots'
4
- option :account
5
- option :project
6
- option :domain
7
- option :listall, default: true
4
+ option :account, desc: "the account associated with the snapshot"
5
+ option :project, desc: "the project associated with the snapshot"
6
+ option :domain, desc: "the domain name of the snapshot's account"
7
+ option :listall, default: true, desc: "list all resources the caller has rights on"
8
+ option :state, desc: "filter snapshots by state"
8
9
  def list
9
10
  resolve_account
10
11
  resolve_project
@@ -13,11 +14,12 @@ class Snapshot < CloudstackCli::Base
13
14
  if snapshots.size < 1
14
15
  say "No snapshots found."
15
16
  else
16
- table = [["Account", "Name", "Volume", "Created", "Type"]]
17
+ table = [%w(Account Name Volume Created Type State)]
18
+ snapshots = filter_by(snapshots, :state, options[:state]) if options[:state]
17
19
  snapshots.each do |snapshot|
18
20
  table << [
19
21
  snapshot['account'], snapshot['name'], snapshot['volumename'],
20
- snapshot['created'], snapshot['snapshottype']
22
+ snapshot['created'], snapshot['snapshottype'], snapshot['state']
21
23
  ]
22
24
  end
23
25
  print_table table
@@ -1,3 +1,5 @@
1
+ require 'thread'
2
+
1
3
  class VirtualMachine < CloudstackCli::Base
2
4
 
3
5
  desc "list", "list virtual machines"
@@ -43,7 +45,10 @@ class VirtualMachine < CloudstackCli::Base
43
45
  puts "No virtual machines found."
44
46
  else
45
47
  print_virtual_machines(virtual_machines)
46
- execute_virtual_machines_commands(virtual_machines) if options[:command]
48
+ execute_virtual_machines_commands(
49
+ options[:command].downcase,
50
+ virtual_machines
51
+ ) if options[:command]
47
52
  end
48
53
  end
49
54
 
@@ -84,7 +89,7 @@ class VirtualMachine < CloudstackCli::Base
84
89
  say "Start deploying virtual machine#{ "s" if names.size > 1 }...", :green
85
90
  jobs = names.map do |name|
86
91
  if virtual_machine = client.list_virtual_machines(name: name, project_id: options[:project_id]).first
87
- say "virtual_machine #{name} (#{virtual_machine["state"]}) already exists.", :yellow
92
+ say "virtual machine #{name} (#{virtual_machine["state"]}) already exists.", :yellow
88
93
  job = {
89
94
  id: 0,
90
95
  name: "Create virtual machine #{name}",
@@ -93,7 +98,7 @@ class VirtualMachine < CloudstackCli::Base
93
98
  else
94
99
  job = {
95
100
  id: client.deploy_virtual_machine(options, {sync: true})['jobid'],
96
- name: "Create virtual_machine #{name}"
101
+ name: "Create virtual machine #{name}"
97
102
  }
98
103
  end
99
104
  job
@@ -116,17 +121,17 @@ class VirtualMachine < CloudstackCli::Base
116
121
  say "Finished.", :green
117
122
  end
118
123
 
119
- desc "destroy NAME [NAME2 ..]", "destroy virtual_machine(s)"
124
+ desc "destroy NAME [NAME2 ..]", "destroy virtual machine(s)"
120
125
  option :project
121
126
  option :force, desc: "destroy without asking", type: :boolean, aliases: '-f'
122
- option :expunge, desc: "expunge virtual_machine immediately", type: :boolean, default: false, aliases: '-E'
127
+ option :expunge, desc: "expunge virtual machine immediately", type: :boolean, default: false, aliases: '-E'
123
128
  def destroy(*names)
124
129
  resolve_project
125
130
  names.each do |name|
126
131
  unless virtual_machine = client.list_virtual_machines(options.merge(name: name, listall: true)).first
127
132
  say "Virtual machine #{name} not found.", :red
128
133
  else
129
- ask = "Destroy #{name} (#{virtual_machine['state']})? [y/N]:"
134
+ ask = "Destroy #{name} (#{virtual machine['state']})? [y/N]:"
130
135
  if options[:force] || yes?(ask, :yellow)
131
136
  say "destroying #{name} "
132
137
  client.destroy_virtual_machine(
@@ -139,12 +144,12 @@ class VirtualMachine < CloudstackCli::Base
139
144
  end
140
145
  end
141
146
 
142
- desc "create_interactive", "interactive creation of a virtual_machine with network access"
147
+ desc "create_interactive", "interactive creation of a virtual machine with network access"
143
148
  def create_interactive
144
149
  bootstrap_server_interactive
145
150
  end
146
151
 
147
- desc "stop NAME", "stop a virtual_machine"
152
+ desc "stop NAME", "stop a virtual machine"
148
153
  option :project
149
154
  option :force
150
155
  def stop(name)
@@ -170,12 +175,12 @@ class VirtualMachine < CloudstackCli::Base
170
175
  say "Virtual machine #{name} not found.", :red
171
176
  exit 1
172
177
  end
173
- say("Starting virtual_machine #{name}", :magenta)
178
+ say("Starting virtual machine #{name}", :magenta)
174
179
  client.start_virtual_machine(id: virtual_machine['id'])
175
180
  puts
176
181
  end
177
182
 
178
- desc "reboot NAME", "reboot a virtual_machine"
183
+ desc "reboot NAME", "reboot a virtual machine"
179
184
  option :project
180
185
  option :force
181
186
  def reboot(name)
@@ -212,7 +217,7 @@ class VirtualMachine < CloudstackCli::Base
212
217
  ]
213
218
  end
214
219
  print_table table
215
- say "Total number of virtual_machines: #{virtual_machines.count}"
220
+ say "Total number of virtual machines: #{virtual_machines.count}"
216
221
  end
217
222
  end
218
223
 
@@ -222,20 +227,17 @@ class VirtualMachine < CloudstackCli::Base
222
227
  exit 1
223
228
  end
224
229
  exit unless yes?("\n#{command.capitalize} the virtual_machine(s) above? [y/N]:", :magenta)
225
- virtual_machines.each_slice(options[:concurrency]) do | batch |
226
- jobs = batch.map do |virtual_machine|
227
- {
228
- id: client.send(
229
- "#{command}_virtual_machine",
230
- { id: virtual_machine['id'] },
231
- { sync: true }
232
- )['jobid'],
233
- name: "#{command.capitalize} virtual_machine #{virtual_machine['name']}"
234
- }
235
- end
236
- puts
237
- watch_jobs(jobs)
230
+
231
+ jobs = virtual_machines.map do |vm|
232
+ {
233
+ job_id: nil,
234
+ object_id: vm["id"],
235
+ name: "#{command.capitalize} virtual machine #{vm['name']}",
236
+ status: -1
237
+ }
238
238
  end
239
+
240
+ run_background_jobs(jobs, "#{command}_virtual_machine")
239
241
  end
240
242
 
241
243
  end # no_commands
@@ -12,44 +12,66 @@ module CloudstackCli
12
12
  end
13
13
 
14
14
  ASYNC_STATES = {
15
- 0 => "running",
16
- 1 => "completed",
17
- 2 => "error"
15
+ -1 => "waiting",
16
+ 0 => "running",
17
+ 1 => "completed",
18
+ 2 => "error"
18
19
  }
19
20
 
20
- def watch_jobs(jobs)
21
- chars = %w(| / - \\)
22
- call = 0
23
- opts = {t_start: Time.now}
24
- jobs = update_job_status(jobs)
25
- while jobs.select{|job| job[:status] == 0}.size > 0 do
26
- if call.modulo(40) == 0
27
- t = Thread.new { jobs = update_job_status(jobs) }
28
- while t.alive?
21
+ def run_background_jobs(jobs, command)
22
+ view_thread = Thread.new do
23
+ chars = %w(| / - \\)
24
+ call = 0
25
+ opts = {t_start: Time.now}
26
+
27
+ while jobs.select{|job| job[:status] < 1 }.size > 0 do
28
+ if call.modulo(40) == 0
29
+ t = Thread.new { jobs = update_jobs(jobs, command) }
30
+ while t.alive?
31
+ chars = print_job_status(jobs, chars,
32
+ call == 0 ? opts.merge(no_clear: true) : opts
33
+ )
34
+ call += 1
35
+ end
36
+ t.join
37
+ else
29
38
  chars = print_job_status(jobs, chars,
30
39
  call == 0 ? opts.merge(no_clear: true) : opts
31
40
  )
32
41
  call += 1
33
42
  end
34
- t.join
35
- else
36
- chars = print_job_status(jobs, chars,
37
- call == 0 ? opts.merge(no_clear: true) : opts
38
- )
39
- call += 1
40
43
  end
44
+ print_job_status(jobs, chars,
45
+ call == 0 ? opts.merge(no_clear: true) : opts
46
+ )
41
47
  end
42
- print_job_status(jobs, chars,
43
- call == 0 ? opts.merge(no_clear: true) : opts
44
- )
48
+ view_thread.join
45
49
  end
46
50
 
47
- def update_job_status(jobs)
48
- jobs.each do |job|
49
- unless job[:status] && job[:status] > 0
50
- job[:status] = client.query_async_job_result(job_id: job[:id])['jobstatus']
51
+ def update_jobs(jobs, command)
52
+ # update running job status
53
+ threads = jobs.select{|job| job[:status] == 0 }.map do |job|
54
+ Thread.new do
55
+ job[:status] = client.query_async_job_result(job_id: job[:job_id])['jobstatus']
56
+ end
57
+ end
58
+ threads.each(&:join)
59
+
60
+ # launch new jobs if required and possible
61
+ launch_capacity = options[:concurrency] - jobs.select{|job| job[:status] == 0 }.count
62
+ threads = []
63
+ jobs.select{|job| job[:status] == -1 }.each do |job|
64
+ if launch_capacity > 0
65
+ threads << Thread.new do
66
+ job[:job_id] = client.send(
67
+ command, { id: job[:object_id] }, { sync: true }
68
+ )['jobid']
69
+ job[:status] = 0
70
+ end
71
+ launch_capacity -= 1
51
72
  end
52
73
  end
74
+ threads.each(&:join)
53
75
  jobs
54
76
  end
55
77
 
@@ -1,3 +1,3 @@
1
1
  module CloudstackCli
2
- VERSION = "1.0.0.rc3"
2
+ VERSION = "1.0.0.rc4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudstack-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc3
4
+ version: 1.0.0.rc4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nik Wolfgramm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-04 00:00:00.000000000 Z
11
+ date: 2015-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc