cloudstack-cli 1.5.9 → 1.5.10

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: c3825bffdc7d610ccd33d5ea01d48804ad3caeaf
4
- data.tar.gz: 23e2821aaf38b61cbc98091a460f91e42d75ebb9
3
+ metadata.gz: 9db726fce7f2ff20082296b4e424db14497a04ab
4
+ data.tar.gz: 6d6177310113255ec6ff7b34fc56dece69ff0c66
5
5
  SHA512:
6
- metadata.gz: 2eca64f2d69c815e19fbf7696087efc5a9a0f3f743acec88c5ea26c8b6a155510f1471b977bac4d6113dd351148abf393402698dec05ab4f938997be8c30a5cb
7
- data.tar.gz: 7c43cc6ce81a7df4c7bffc94a967786fc76ff50da12951ec2eafda751e9da1541a83263c4c69870f525338e9687715b4a0b7c0f1244df99a540d680fab880ad2
6
+ metadata.gz: 2e6177a5cd286a74c2374ef3171d8c886e36e7abb94ded0e482bf1d4e86613ba56b103c00963fcfe30e6aad1bdfa49c2d659fd1bf5721a8cf81f94c0a8c89714
7
+ data.tar.gz: a09c1c2ceed2856f46f5cececfd61248bb87fd87d7b5f62d0d8fe49bedbcde4ab94b98fd6cf7b87e308cdf5a6cbeb5222a5890a215bbf1956ad36d958cda0b21
data/Gemfile.lock CHANGED
@@ -6,7 +6,7 @@ PATH
6
6
  PATH
7
7
  remote: .
8
8
  specs:
9
- cloudstack-cli (1.5.9)
9
+ cloudstack-cli (1.5.10)
10
10
  cloudstack_client (~> 1.4.3)
11
11
  thor (~> 0.19)
12
12
 
@@ -25,4 +25,4 @@ DEPENDENCIES
25
25
  rake (~> 11.3)
26
26
 
27
27
  BUNDLED WITH
28
- 1.13.2
28
+ 1.13.6
@@ -10,10 +10,13 @@ class PortRule < CloudstackCli::Base
10
10
  option :keyword, desc: "list by keyword"
11
11
  def create(server_name)
12
12
  resolve_project
13
- unless server = client.list_virtual_machines(name: server_name, project_id: options[:project_id]).first
13
+ unless server = client.list_virtual_machines(
14
+ name: server_name, project_id: options[:project_id], listall: true
15
+ ).find {|vm| vm["name"] == server_name }
14
16
  error "Server #{server_name} not found."
15
17
  exit 1
16
18
  end
19
+ ip_addr = nil
17
20
  options[:rules].each do |pf_rule|
18
21
  ip = pf_rule.split(":")[0]
19
22
  unless ip == ''
@@ -23,16 +26,13 @@ class PortRule < CloudstackCli::Base
23
26
  end
24
27
  else
25
28
  say "Assign a new IP address ", :yellow
26
- say(" OK", :green) if ip_addr = client.associate_ip_address(
27
- networkid: client.list_networks(
28
- project_id: options[:project_id]
29
- ).find {|n| n['name'] == options[:network]}['id'],
30
- project_id: options[:project_id]
31
- )
29
+ net_id = client.list_networks(project_id: options[:project_id]).find {|n| n['name'] == options[:network]}['id']
30
+ say(" OK", :green) if ip_addr = client.associate_ip_address(networkid: net_id)["ipaddress"]
32
31
  end
33
32
  port = pf_rule.split(":")[1]
34
33
  say "Create port forwarding rule #{ip_addr["ipaddress"]}:#{port} for server #{server_name} ", :yellow
35
- say(" OK.", :green) if client.create_port_forwarding_rule(
34
+
35
+ say(" OK", :green) if client.create_port_forwarding_rule(
36
36
  ipaddress_id: ip_addr["id"],
37
37
  public_port: port,
38
38
  private_port: port,
@@ -13,7 +13,7 @@ class Router < CloudstackCli::Base
13
13
  desc: "command to execute for each router",
14
14
  enum: %w(START STOP REBOOT STOP_START)
15
15
  option :concurrency, type: :numeric, default: 10, aliases: '-C',
16
- desc: "number of concurrent command to execute"
16
+ desc: "number of concurrent commands to execute"
17
17
  option :format, default: "table",
18
18
  enum: %w(table json yaml)
19
19
  option :showid, type: :boolean, desc: "display the router ID"
@@ -43,7 +43,7 @@ class Router < CloudstackCli::Base
43
43
  desc: "command to execute for each router",
44
44
  enum: %w(START STOP REBOOT)
45
45
  option :concurrency, type: :numeric, default: 10, aliases: '-C',
46
- desc: "number of concurrent command to execute"
46
+ desc: "number of concurrent commands to execute"
47
47
  option :format, default: "table",
48
48
  enum: %w(table json yaml)
49
49
  def list_from_file(file)
@@ -219,7 +219,7 @@ class Router < CloudstackCli::Base
219
219
  jobs = routers.map do |router|
220
220
  {
221
221
  job_id: nil,
222
- object_id: router["id"],
222
+ args: { id: router["id"] },
223
223
  name: "#{cmd.capitalize} router #{router['name']}",
224
224
  status: -1
225
225
  }
@@ -6,25 +6,28 @@ class Stack < CloudstackCli::Base
6
6
  option :skip_forwarding_rules, default: false,
7
7
  type: :boolean, aliases: '-s',
8
8
  desc: "Skip creation of port forwarding rules."
9
+ option :concurrency, type: :numeric, default: 10, aliases: '-C',
10
+ desc: "number of concurrent commands to execute"
9
11
  def create(stackfile)
10
12
  stack = parse_file(stackfile)
11
13
  project_id = find_project_by_name(stack["project"])
14
+
12
15
  say "Create stack #{stack["name"]}...", :green
13
16
  jobs = []
14
17
  stack["servers"].each do |instance|
15
18
  string_to_array(instance["name"]).each do |name|
16
19
  if !options[:limit] || options[:limit].include?(name)
17
- server = client.list_virtual_machines(name: name, project_id: project_id).first
18
- if server
20
+ if server = client.list_virtual_machines(
21
+ name: name, project_id: project_id, listall: true
22
+ ).find {|vm| vm["name"] == name }
19
23
  say "VM #{name} (#{server["state"]}) already exists.", :yellow
20
24
  jobs << {
21
25
  id: 0,
22
26
  name: "Create VM #{name}",
23
- status: 1
27
+ status: 3
24
28
  }
25
29
  else
26
30
  options.merge!({
27
- name: name,
28
31
  displayname: instance["decription"],
29
32
  zone: instance["zone"] || stack["zone"],
30
33
  project: stack["project"],
@@ -39,36 +42,55 @@ class Stack < CloudstackCli::Base
39
42
  keypair: instance["keypair"] || stack["keypair"],
40
43
  ip_address: instance["ip_address"]
41
44
  })
45
+ vm_options_to_params
42
46
  jobs << {
43
- id: client.deploy_virtual_machine(
44
- vm_options_to_params,
45
- {sync: true}
46
- )['jobid'],
47
- name: "Create VM #{name}"
47
+ job_id: nil,
48
+ args: options.merge(name: name),
49
+ name: "Create VM #{name}",
50
+ status: -1
48
51
  }
49
52
  end
50
53
  end
51
54
  end
52
55
  end
53
- watch_jobs(jobs)
54
56
 
55
- unless options[:skip_forwarding_rules]
57
+ if jobs.count{|job| job[:status] < 1 } > 0
58
+ run_background_jobs(jobs, "deploy_virtual_machine")
59
+ end
60
+
61
+ # count jobs with status 1 => Completed
62
+ successful_jobs = jobs.count {|job| job[:status] == 1 }
63
+ unless successful_jobs == 0 || options[:skip_forwarding_rules]
56
64
  say "Check for port forwarding rules...", :green
57
- jobs = []
58
- stack["servers"].each do |instance|
59
- string_to_array(instance["name"]).each do |name|
60
- if (!options[:limit] || options[:limit].include?(name)) && port_rules = string_to_array(instance["port_rules"])
61
- server = client.list_virtual_machines(name: name, project_id: project_id).first
62
- create_port_rules(server, port_rules, false).each_with_index do |job_id, index|
63
- job_name = "Create port forwarding rules (#{port_rules[index]}) for VM #{name}"
64
- jobs << {id: job_id, name: job_name}
65
- end
65
+ pjobs = []
66
+ jobs.select{|job| job[:status] == 1}.each do |job|
67
+ vm = job[:result]["virtualmachine"]
68
+ vm_def = find_vm_in_stack(vm["name"], stack)
69
+ if port_rules = string_to_array(vm_def["port_rules"])
70
+ create_port_rules(vm, port_rules, false).each_with_index do |job_id, index|
71
+ job_name = "Create port forwarding rules (#{port_rules[index]}) for VM #{vm["name"]}"
72
+ pjobs << {id: job_id, name: job_name}
66
73
  end
67
74
  end
68
75
  end
69
- watch_jobs(jobs)
76
+ watch_jobs(pjobs)
77
+ pjobs.each do |job|
78
+ if job[:result]
79
+ result = job[:result]["portforwardingrule"]
80
+ puts "Created port forwarding rule #{result['ipaddress']}:#{result['publicport']} => #{result['privateport']} for VM #{result['virtualmachinename']}"
81
+ end
82
+ end
70
83
  end
71
84
  say "Finished.", :green
85
+
86
+ if successful_jobs > 0 && yes?("Display password(s) for VM(s)? [y/N]:", :yellow)
87
+ pw_table = jobs.select {|job| job[:status] == 1 && job[:result] }.map do |job|
88
+ if result = job[:result]["virtualmachine"]
89
+ ["#{result["name"]}:", result["password"] || "n/a"]
90
+ end
91
+ end
92
+ print_table(pw_table) if pw_table.size > 0
93
+ end
72
94
  end
73
95
 
74
96
  desc "destroy STACKFILE", "destroy a stack of VMs"
@@ -101,10 +123,13 @@ class Stack < CloudstackCli::Base
101
123
  exit
102
124
  end
103
125
 
104
- if options[:force] || yes?("Destroy the following VM #{servers.join(', ')}? [y/N]:", :yellow)
126
+ if options[:force] ||
127
+ yes?("Destroy #{'and expunge ' if options[:expunge]}the following VM(s)? #{servers.join(', ')} [y/N]:", :yellow)
105
128
  jobs = []
106
129
  servers.each do |name|
107
- if server = client.list_virtual_machines(name: name, project_id: project_id).first
130
+ if server = client.list_virtual_machines(
131
+ name: name, project_id: project_id, listall: true
132
+ ).find {|vm| vm["name"] == name }
108
133
  jobs << {
109
134
  id: client.destroy_virtual_machine(
110
135
  { id: server['id'], expunge: options[:expunge] },
@@ -142,6 +167,14 @@ class Stack < CloudstackCli::Base
142
167
  string ? string.gsub(', ', ',').split(',') : nil
143
168
  end
144
169
 
170
+ def find_vm_in_stack(name, stack)
171
+ stack["servers"].each do |server|
172
+ if string_to_array(server["name"]).find{|n| n == name }
173
+ return server
174
+ end
175
+ end
176
+ end
177
+
145
178
  end # no_commands
146
179
 
147
180
  end
@@ -15,7 +15,7 @@ class VirtualMachine < CloudstackCli::Base
15
15
  desc: "command to execute for the given virtual machines",
16
16
  enum: %w(START STOP REBOOT)
17
17
  option :concurrency, type: :numeric, default: 10, aliases: '-C',
18
- desc: "number of concurrent command to execute"
18
+ desc: "number of concurrent commands to execute"
19
19
  option :format, default: "table",
20
20
  enum: %w(table json yaml)
21
21
  def list
@@ -78,7 +78,7 @@ class VirtualMachine < CloudstackCli::Base
78
78
  option :zone, aliases: '-z', required: true, desc: "availability zone name"
79
79
  option :networks, aliases: '-n', type: :array, desc: "network names"
80
80
  option :project, aliases: '-p', desc: "project name"
81
- option :port_rules, aliases: '-pr', type: :array,
81
+ option :port_rules, type: :array,
82
82
  default: [],
83
83
  desc: "Port Forwarding Rules [public_ip]:port ..."
84
84
  option :disk_offering, desc: "disk offering (data disk for template, root disk for iso)"
@@ -91,6 +91,8 @@ class VirtualMachine < CloudstackCli::Base
91
91
  option :ip_network_list, desc: "ip_network_list (net1:ip net2:ip...)", type: :array
92
92
  option :user_data,
93
93
  desc: "optional binary data that can be sent to the virtual machine upon a successful deployment."
94
+ option :concurrency, type: :numeric, default: 10, aliases: '-C',
95
+ desc: "number of concurrent commands to execute"
94
96
  def create(*names)
95
97
  if names.size == 0
96
98
  say "Please provide at least one virtual machine name.", :yellow
@@ -106,39 +108,45 @@ class VirtualMachine < CloudstackCli::Base
106
108
  jobs = names.map do |name|
107
109
  if virtual_machine = find_vm_by_name(name)
108
110
  say "virtual machine #{name} (#{virtual_machine["state"]}) already exists.", :yellow
109
- job = {id: 0, name: "Create virtual machine #{name}", status: 3}
111
+ job = {name: "Create virtual machine #{name}", status: 3}
110
112
  else
111
113
  job = {
112
- id: client.deploy_virtual_machine(options.merge(name: name), {sync: true})['jobid'],
113
- name: "Create virtual machine #{name}"
114
+ args: options.merge(name: name),
115
+ name: "Create VM #{name}",
116
+ status: -1
114
117
  }
115
118
  end
116
119
  job
117
120
  end
118
- watch_jobs(jobs)
121
+
122
+ if jobs.count{|job| job[:status] < 1 } > 0
123
+ run_background_jobs(jobs, "deploy_virtual_machine")
124
+ end
125
+
119
126
  successful_jobs = jobs.count {|job| job[:status] == 1 }
120
127
  if options[:port_rules].size > 0 && successful_jobs > 0
121
128
  say "Create port forwarding rules...", :green
122
129
  pjobs = []
123
- names.each do |name|
124
- vm = client.list_virtual_machines(name: name, project_id: options[:project_id]).first
130
+ jobs.select{|job| job[:status] == 1}.each do |job|
131
+ vm = job[:result]["virtualmachine"]
125
132
  create_port_rules(vm, options[:port_rules], false).each_with_index do |job_id, index|
126
133
  pjobs << {
127
134
  id: job_id,
128
- name: "Create port forwarding ##{index + 1} rules for VM #{vm['name']}"
135
+ name: "Create port forwarding rule #{options[:port_rules][index]} for VM #{vm['name']}"
129
136
  }
130
137
  end
131
138
  end
132
139
  watch_jobs(pjobs)
133
140
  end
134
141
  say "Finished.", :green
142
+
135
143
  if successful_jobs > 0 && yes?("Display password(s) for VM(s)? [y/N]:", :yellow)
136
- table = []
137
- jobs.each do |job|
138
- data = client.query_async_job_result(jobid: job[:id])["jobresult"]["virtualmachine"] rescue nil
139
- table << ["#{data["name"]}:", data["password"]] if data
144
+ pw_table = jobs.select {|job| job[:status] == 1 && job[:result] }.map do |job|
145
+ if result = job[:result]["virtualmachine"]
146
+ ["#{result["name"]}:", result["password"] || "n/a"]
147
+ end
140
148
  end
141
- print_table table
149
+ print_table(pw_table) if pw_table.size > 0
142
150
  end
143
151
  end
144
152
 
@@ -319,7 +327,7 @@ class VirtualMachine < CloudstackCli::Base
319
327
  jobs = virtual_machines.map do |vm|
320
328
  {
321
329
  job_id: nil,
322
- object_id: vm["id"],
330
+ args: { id: vm["id"] },
323
331
  name: "#{command.capitalize} virtual machine #{vm['name']}",
324
332
  status: -1
325
333
  }
@@ -24,7 +24,7 @@ module CloudstackCli
24
24
  call = 0
25
25
  opts = {t_start: Time.now}
26
26
  jobs = update_job_status(jobs)
27
- while jobs.select{|job| job[:status].to_i < 1 }.size > 0 do
27
+ while jobs.count{|job| job[:status].to_i < 1 } > 0 do
28
28
  if call.modulo(40) == 0
29
29
  t = Thread.new { jobs = update_job_status(jobs) }
30
30
  while t.alive?
@@ -50,7 +50,12 @@ module CloudstackCli
50
50
  jobs.each do |job|
51
51
  job[:status] = 0 unless job[:status]
52
52
  if job[:status] == 0
53
- job[:status] = client.query_async_job_result(job_id: job[:id])['jobstatus']
53
+ result = client.query_async_job_result(job_id: job[:id])
54
+ job[:status] = result["jobstatus"]
55
+ # add result information for terminated jobs
56
+ if job[:status].between?(1, 2)
57
+ job[:result] = result["jobresult"]
58
+ end
54
59
  end
55
60
  end
56
61
  jobs
@@ -62,7 +67,7 @@ module CloudstackCli
62
67
  call = 0
63
68
  opts = {t_start: Time.now}
64
69
 
65
- while jobs.select{|job| job[:status] < 1 }.size > 0 do
70
+ while jobs.count{|job| job[:status] < 1 } > 0 do
66
71
  if call.modulo(40) == 0
67
72
  t = Thread.new { jobs = update_jobs(jobs, command) }
68
73
  while t.alive?
@@ -90,19 +95,23 @@ module CloudstackCli
90
95
  # update running job status
91
96
  threads = jobs.select{|job| job[:status] == 0 }.map do |job|
92
97
  Thread.new do
93
- job[:status] = client.query_async_job_result(job_id: job[:job_id])['jobstatus']
98
+ result = client.query_async_job_result(job_id: job[:job_id])
99
+ job[:status] = result['jobstatus']
100
+ if job[:status].between?(1, 2)
101
+ job[:result] = result["jobresult"]
102
+ end
94
103
  end
95
104
  end
96
105
  threads.each(&:join)
97
106
 
98
107
  # launch new jobs if required and possible
99
- launch_capacity = options[:concurrency] - jobs.select{|job| job[:status] == 0 }.count
108
+ launch_capacity = (options[:concurrency] ||= 10) - jobs.count{|job| job[:status] == 0 }
100
109
  threads = []
101
110
  jobs.select{|job| job[:status] == -1 }.each do |job|
102
111
  if launch_capacity > 0
103
112
  threads << Thread.new do
104
113
  job[:job_id] = client.send(
105
- command, { id: job[:object_id] }, { sync: true }
114
+ command, job[:args], { sync: true }
106
115
  )['jobid']
107
116
  job[:status] = 0
108
117
  end
@@ -120,7 +129,7 @@ module CloudstackCli
120
129
  puts job[:status] == 0 ? spinner.first : ""
121
130
  end
122
131
  t_elapsed = opts[:t_start] ? (Time.now - opts[:t_start]).round(1) : 0
123
- completed = jobs.select{|j| j[:status] == 1}.size
132
+ completed = jobs.count{|j| j[:status] == 1 }
124
133
  say "Completed: #{completed} of #{jobs.size} (#{t_elapsed}s)", :magenta
125
134
  sleep opts[:sleeptime] || 0.1
126
135
  spinner.push spinner.shift
@@ -18,6 +18,7 @@ module CloudstackCli
18
18
  else
19
19
  resolve_networks
20
20
  end
21
+ options
21
22
  end
22
23
 
23
24
  def resolve_zone
@@ -1,3 +1,3 @@
1
1
  module CloudstackCli
2
- VERSION = "1.5.9"
2
+ VERSION = "1.5.10"
3
3
  end
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: "web_stack_b"
3
+ description: "Web Application Stack"
4
+ version: "1.0"
5
+ zone: "ZUERICH_EQ"
6
+ project: "Playground"
7
+ group: "my_web_stack"
8
+ servers:
9
+ - name: "webx-01, webx-02"
10
+ description: "Web nodes"
11
+ template: "CentOS-7-x86_64"
12
+ offering: "1cpu_1gb"
13
+ networks: "M_PLAY"
14
+ port_rules: ":80, :443"
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.5.9
4
+ version: 1.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nik Wolfgramm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-22 00:00:00.000000000 Z
11
+ date: 2016-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -111,6 +111,7 @@ files:
111
111
  - test/ma-test.yml
112
112
  - test/stack_example.json
113
113
  - test/stack_example.yml
114
+ - test/stack_example_b.yml
114
115
  homepage: http://github.com/niwo/cloudstack-cli
115
116
  licenses:
116
117
  - MIT
@@ -141,3 +142,4 @@ test_files:
141
142
  - test/ma-test.yml
142
143
  - test/stack_example.json
143
144
  - test/stack_example.yml
145
+ - test/stack_example_b.yml