cloudstack-cli 1.0.0.rc3 → 1.0.0.rc4

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