cloudstack-cli 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloudstack-cli (0.3.3)
4
+ cloudstack-cli (0.3.4)
5
5
  thor (~> 0.18.1)
6
6
 
7
7
  GEM
@@ -1,15 +1,15 @@
1
1
  class Capacity < CloudstackCli::Base
2
2
  CAPACITY_TYPES = {
3
- 0 => "Memory",
4
- 1 => "CPU",
5
- 2 => "Storage",
6
- 3 => "Storage Allocated",
7
- 4 => "Public IP's",
8
- 5 => "Private IP's",
9
- 6 => "Secondary Storage",
10
- 7 => "VLAN",
11
- 8 => "Direct Attached Public IP's",
12
- 9 => "Local Storage"
3
+ 0 => {name: "Memory", unit: "GB", divider: 1024.0**3},
4
+ 1 => {name: "CPU", unit: "GHz", divider: 1000.0},
5
+ 2 => {name: "Storage", unit: "TB", divider: 1024.0**4},
6
+ 3 => {name: "Primary Storage", unit: "TB", divider: 1024.0**4},
7
+ 4 => {name: "Public IP's"},
8
+ 5 => {name: "Private IP's"},
9
+ 6 => {name: "Secondary Storage", unit: "TB", divider: 1024.0**4},
10
+ 7 => {name: "VLAN"},
11
+ 8 => {name: "Direct Attached Public IP's"},
12
+ 9 => {name: "Local Storage", unit: "TB", divider: 1024.0**4}
13
13
  }
14
14
 
15
15
  desc "list", "list system capacity"
@@ -21,9 +21,9 @@ class Capacity < CloudstackCli::Base
21
21
  capacities.each do |c|
22
22
  table << [
23
23
  c['zonename'],
24
- CAPACITY_TYPES[c['type']],
25
- c['capacityused'],
26
- c['capacitytotal'],
24
+ CAPACITY_TYPES[c['type']][:name],
25
+ capacity_to_s(c, 'capacityused'),
26
+ capacity_to_s(c, 'capacitytotal'),
27
27
  "#{c['percentused']}%"
28
28
  ]
29
29
  end
@@ -31,4 +31,17 @@ class Capacity < CloudstackCli::Base
31
31
  print_table table
32
32
  end
33
33
 
34
+ no_commands do
35
+
36
+ def capacity_to_s(capacity, entity)
37
+ value = CAPACITY_TYPES[capacity['type']][:divider] ?
38
+ (capacity[entity] / CAPACITY_TYPES[capacity['type']][:divider]).round(1) :
39
+ capacity[entity]
40
+ CAPACITY_TYPES[capacity['type']][:unit] ?
41
+ "#{value}#{CAPACITY_TYPES[capacity['type']][:unit]}" :
42
+ value.to_s
43
+ end
44
+
45
+ end
46
+
34
47
  end
@@ -33,7 +33,8 @@ class Network < CloudstackCli::Base
33
33
  elsif options[:account]
34
34
  networks = client.list_networks(account: options[:account])
35
35
  else
36
- networks = client.list_networks(project_id: -1, isdefault: options[:isdefault])
36
+ networks = client.list_networks(isdefault: options[:isdefault])
37
+ networks += client.list_networks(project_id: -1, isdefault: options[:isdefault])
37
38
  end
38
39
 
39
40
  if networks.size < 1
@@ -18,6 +18,10 @@ class PortRule < CloudstackCli::Base
18
18
  ip = pf_rule.split(":")[0]
19
19
  if ip != ''
20
20
  ip_addr = client.get_public_ip_address(ip)
21
+ unless ip_addr
22
+ say "Error: IP #{ip} not found.", :red
23
+ next
24
+ end
21
25
  else
22
26
  ip_addr = frontendip ||= client.associate_ip_address(
23
27
  client.get_network(options[:network], project ? project["id"] : nil)["id"]
@@ -47,8 +47,42 @@ class Server < CloudstackCli::Base
47
47
  option :keypair, desc: "the name of the ssh keypair to use"
48
48
  option :group, desc: "group name"
49
49
  option :account, desc: "account name"
50
- def create(name)
51
- bootstrap_server(options.merge({name: name}))
50
+ def create(*names)
51
+ projectid = find_project['id'] if options[:project]
52
+ say "Start deploying servers...", :green
53
+ jobs = names.map do |name|
54
+ server = client(quiet: true).get_server(name, projectid)
55
+ if server
56
+ say "Server #{name} (#{server["state"]}) already exists.", :yellow
57
+ job = {
58
+ id: 0,
59
+ name: "Create server #{name}",
60
+ status: 1
61
+ }
62
+ else
63
+ job = {
64
+ id: client.create_server(options.merge({name: name, sync: true}))['jobid'],
65
+ name: "Create server #{name}"
66
+ }
67
+ end
68
+ job
69
+ end
70
+ watch_jobs(jobs)
71
+ if options[:port_rules].size > 0
72
+ say "Create port forwarding rules...", :green
73
+ jobs = []
74
+ names.each do |name|
75
+ server = client(quiet: true).get_server(name, projectid)
76
+ create_port_rules(server, options[:port_rules], false).each_with_index do |job_id, index|
77
+ jobs << {
78
+ id: job_id,
79
+ name: "Create port forwarding ##{index + 1} rules for server #{server['name']}"
80
+ }
81
+ end
82
+ end
83
+ watch_jobs(jobs)
84
+ end
85
+ say "Finished.", :green
52
86
  end
53
87
 
54
88
  desc "destroy NAME [NAME2 ..]", "destroy server(s)"
@@ -20,6 +20,7 @@ class SshKeyPair < CloudstackCli::Base
20
20
  desc 'create NAME', 'create ssh key pair'
21
21
  option :account
22
22
  option :project
23
+ option :public_key
23
24
  def create(name)
24
25
  pair = client.create_ssh_key_pair(name, options)
25
26
  say "Name : #{pair['name']}"
@@ -3,30 +3,63 @@ class Stack < CloudstackCli::Base
3
3
  desc "create STACKFILE", "create a stack of servers"
4
4
  def create(stackfile)
5
5
  stack = parse_stackfile(stackfile)
6
- say "Create stack #{stack["name"]}..."
7
- threads = []
8
- stack["servers"].each do |server|
9
- server["name"].split(', ').each_with_index do |name, i|
10
- threads << Thread.new(i) {
11
- bootstrap_server(
12
- name: name,
13
- displayname: server["decription"],
14
- zone: server["zone"] || stack["zone"],
15
- template: server["template"],
16
- iso: server["iso"] ,
17
- offering: server["offering"],
18
- networks: string_to_array(server["networks"]),
19
- port_rules: string_to_array(server["port_rules"]),
20
- project: stack["project"],
21
- disk_offering: server["disk_offering"],
22
- disk_size: server["disk_size"],
23
- group: server["group"] || stack["group"],
24
- keypair: server["keypair"] || stack["keypair"]
25
- )
26
- }
6
+ say "Create stack #{stack["name"]}...", :green
7
+ projectid = find_project(stack["project"])['id'] if stack["project"]
8
+ jobs = []
9
+ client.verbose = false
10
+ stack["servers"].each do |instance|
11
+ instance["name"].gsub(', ', ',').split(',').each do |name|
12
+ server = client.get_server(name, projectid)
13
+ if server
14
+ say "Server #{name} (#{server["state"]}) already exists.", :yellow
15
+ jobs << {
16
+ id: 0,
17
+ name: "Create server #{name}",
18
+ status: 1
19
+ }
20
+ else
21
+ jobs << {
22
+ id: client.create_server(
23
+ {
24
+ name: name,
25
+ displayname: instance["decription"],
26
+ zone: instance["zone"] || stack["zone"],
27
+ template: instance["template"],
28
+ iso: instance["iso"] ,
29
+ offering: instance["offering"],
30
+ networks: string_to_array(instance["networks"]),
31
+ project: stack["project"],
32
+ disk_offering: instance["disk_offering"],
33
+ disk_size: instance["disk_size"],
34
+ group: instance["group"] || stack["group"],
35
+ keypair: instance["keypair"] || stack["keypair"],
36
+ sync: true
37
+ }
38
+ )['jobid'],
39
+ name: "Create server #{name}"
40
+ }
41
+ end
42
+ end
43
+ end
44
+ watch_jobs(jobs)
45
+
46
+ say "Check for port forwarding rules...", :green
47
+ jobs = []
48
+ stack["servers"].each do |instance|
49
+ instance["name"].gsub(', ', ',').split(',').each do |name|
50
+ if port_rules = string_to_array(instance["port_rules"])
51
+ server = client(quiet: true).get_server(name, projectid)
52
+ create_port_rules(server, port_rules, false).each_with_index do |job_id, index|
53
+ jobs << {
54
+ id: job_id,
55
+ name: "Create port forwarding ##{index + 1} rules for server #{name}"
56
+ }
57
+ end
58
+ end
27
59
  end
28
60
  end
29
- threads.each {|t| t.join }
61
+ watch_jobs(jobs)
62
+ say "Finished.", :green
30
63
  end
31
64
 
32
65
  desc "destroy STACKFILE", "destroy a stack of servers"
@@ -37,13 +70,29 @@ class Stack < CloudstackCli::Base
37
70
  aliases: '-f'
38
71
  def destroy(stackfile)
39
72
  stack = parse_stackfile(stackfile)
73
+ projectid = find_project(stack["project"])['id'] if stack["project"]
74
+ client.verbose = false
40
75
  servers = []
41
- server = stack["servers"].collect do |server|
42
- server["name"].split(', ').each {|name| servers << name}
76
+ stack["servers"].each do |server|
77
+ server["name"].gsub(', ', ',').split(',').each {|name| servers << name}
78
+ end
79
+
80
+ if options[:force] || yes?("Destroy the following servers #{servers.join(', ')}?", :yellow)
81
+ jobs = []
82
+ servers.each do |name|
83
+ server = client(quiet: true).get_server(name, projectid)
84
+ if server
85
+ jobs << {
86
+ id: client.destroy_server(
87
+ server['id'], false
88
+ )['jobid'],
89
+ name: "Destroy server #{name}"
90
+ }
91
+ end
92
+ end
93
+ watch_jobs(jobs)
94
+ say "Finished.", :green
43
95
  end
44
- say "Destroy stack #{stack["name"]}...", :yellow
45
- puts
46
- invoke "server:destroy", servers, project: stack["project"], force: options[:force]
47
96
  end
48
97
 
49
98
  no_commands do
@@ -59,7 +108,7 @@ class Stack < CloudstackCli::Base
59
108
  end
60
109
 
61
110
  def string_to_array(string)
62
- string ? string.gsub(', ', ',').split(', ') : nil
111
+ string ? string.gsub(', ', ',').split(',') : nil
63
112
  end
64
113
  end
65
114
 
@@ -11,12 +11,6 @@ module CloudstackCli
11
11
  number < 0 ? 0 : number
12
12
  end
13
13
 
14
- def get_async_job_status(ids)
15
- ids.map do |id|
16
- client.query_job(id)['jobstatus']
17
- end
18
- end
19
-
20
14
  ASYNC_STATES = {
21
15
  0 => "running",
22
16
  1 => "completed",
@@ -25,32 +19,45 @@ module CloudstackCli
25
19
 
26
20
  def watch_jobs(jobs)
27
21
  chars = %w(| / - \\)
28
- status = get_async_job_status(jobs.map {|job| job[:id]})
29
22
  call = 0
30
23
  opts = {t_start: Time.now}
31
- puts
32
- while status.include?(0) do
24
+ jobs = update_job_status(jobs)
25
+ while jobs.select{|job| job[:status] == 0}.size > 0 do
33
26
  if call.modulo(40) == 0
34
- t = Thread.new { status = get_async_job_status(jobs.map {|job| job[:id]}) }
27
+ t = Thread.new { jobs = update_job_status(jobs) }
35
28
  while t.alive?
36
- chars = print_job_status(jobs, status, chars, opts)
29
+ chars = print_job_status(jobs, chars,
30
+ call == 0 ? opts.merge(no_clear: true) : opts
31
+ )
32
+ call += 1
37
33
  end
38
34
  t.join
39
35
  else
40
- chars = print_job_status(jobs, status, chars,
36
+ chars = print_job_status(jobs, chars,
41
37
  call == 0 ? opts.merge(no_clear: true) : opts
42
38
  )
43
39
  call += 1
44
40
  end
45
41
  end
46
- print_job_status(jobs, status, chars, opts)
42
+ print_job_status(jobs, chars,
43
+ call == 0 ? opts.merge(no_clear: true) : opts
44
+ )
47
45
  end
48
46
 
49
- def print_job_status(jobs, status, spinner, opts = {t_start: Time.now})
50
- print ("\r" + "\e[A\e[K" * (status.size + 1)) unless opts[:no_clear]
51
- status.each_with_index do |job_status, i|
52
- print "#{jobs[i][:name]} : job #{ASYNC_STATES[job_status]} "
53
- puts job_status == 0 ? spinner.first : ""
47
+ def update_job_status(jobs)
48
+ jobs.each do |job|
49
+ unless job[:status] && job[:status] > 0
50
+ job[:status] = client.query_job(job[:id])['jobstatus']
51
+ end
52
+ end
53
+ jobs
54
+ end
55
+
56
+ def print_job_status(jobs, spinner, opts = {t_start: Time.now})
57
+ print ("\r" + "\e[A\e[K" * (jobs.size + 1)) unless opts[:no_clear]
58
+ jobs.each_with_index do |job, i|
59
+ print "#{job[:name]} : job #{ASYNC_STATES[job[:status]]} "
60
+ puts job[:status] == 0 ? spinner.first : ""
54
61
  end
55
62
  t_elapsed = opts[:t_start] ? (Time.now - opts[:t_start]).round(1) : 0
56
63
  puts "Runtime: #{t_elapsed}s"
@@ -81,21 +88,49 @@ module CloudstackCli
81
88
  server
82
89
  end
83
90
 
84
- def create_port_rules(server, port_rules)
91
+ def create_server(args = {})
92
+ if args[:project] && project = client(quiet: true).get_project(args[:project])
93
+ project_id = project["id"]
94
+ project_name = project['name']
95
+ end
96
+ server = client(quiet: true).get_server(args[:name], project_id)
97
+ unless server
98
+ server = client.create_server(args)
99
+ end
100
+ server
101
+ end
102
+
103
+ def create_port_rules(server, port_rules, async = true)
85
104
  frontendip = nil
105
+ jobs = []
106
+ client.verbose = async
86
107
  port_rules.each do |pf_rule|
87
108
  ip = pf_rule.split(":")[0]
88
109
  if ip != ''
89
110
  ip_addr = client.get_public_ip_address(ip)
111
+ unless ip_addr
112
+ say "Error: IP #{ip} not found.", :red
113
+ next
114
+ end
90
115
  else
91
116
  ip_addr = frontendip ||= client.associate_ip_address(
92
117
  server["nic"].first["networkid"]
93
118
  )
94
119
  end
95
120
  port = pf_rule.split(":")[1]
96
- say "Create port forwarding rule #{ip_addr['ipaddress']}:#{port} for server #{server["name"]}.", :yellow
97
- client.create_port_forwarding_rule(ip_addr["id"], port, 'TCP', port, server["id"])
121
+ if async
122
+ say "Create port forwarding rule #{ip_addr['ipaddress']}:#{port} for server #{server["name"]}.", :yellow
123
+ client.create_port_forwarding_rule(ip_addr["id"], port, 'TCP', port, server["id"])
124
+ return
125
+ else
126
+ jobs << client.create_port_forwarding_rule(
127
+ ip_addr["id"],
128
+ port, 'TCP', port, server["id"],
129
+ false
130
+ )['jobid']
131
+ end
98
132
  end
133
+ jobs
99
134
  end
100
135
 
101
136
  def bootstrap_server_interactive
@@ -1,3 +1,3 @@
1
1
  module CloudstackCli
2
- VERSION = "0.3.4"
2
+ VERSION = "0.3.5"
3
3
  end
@@ -20,12 +20,14 @@ module CloudstackClient
20
20
  include Object.const_get("CloudstackClient").const_get(module_name)
21
21
  end
22
22
 
23
+ attr_accessor :verbose
24
+
23
25
  def initialize(api_url, api_key, secret_key, opts = {})
24
26
  @api_url = api_url
25
27
  @api_key = api_key
26
28
  @secret_key = secret_key
27
29
  @use_ssl = api_url.start_with? "https"
28
- @verbose = !opts[:quiet]
30
+ @verbose = opts[:quiet] ? false : true
29
31
  end
30
32
 
31
33
  ##
@@ -32,7 +32,7 @@ module CloudstackClient
32
32
  ##
33
33
  # Creates a port forwarding rule.
34
34
 
35
- def create_port_forwarding_rule(ip_address_id, private_port, protocol, public_port, virtual_machine_id)
35
+ def create_port_forwarding_rule(ip_address_id, private_port, protocol, public_port, virtual_machine_id, async = true)
36
36
  params = {
37
37
  'command' => 'createPortForwardingRule',
38
38
  'ipAddressId' => ip_address_id,
@@ -41,8 +41,7 @@ module CloudstackClient
41
41
  'publicPort' => public_port,
42
42
  'virtualMachineId' => virtual_machine_id
43
43
  }
44
- json = send_async_request(params)
45
- json['portforwardingrule']
44
+ async ? send_async_request(params)['portforwardingrule'] : send_request(params)
46
45
  end
47
46
 
48
47
  end
@@ -193,7 +193,6 @@ module CloudstackClient
193
193
  network['id']
194
194
  }
195
195
 
196
-
197
196
  params = {
198
197
  'command' => 'deployVirtualMachine',
199
198
  'serviceOfferingId' => service['id'],
@@ -220,8 +219,7 @@ module CloudstackClient
220
219
  params['account'] = args[:account]
221
220
  end
222
221
 
223
- json = send_async_request(params)
224
- json['virtualmachine']
222
+ args[:sync] ? send_request(params) : send_async_request(params)['virtualmachine']
225
223
  end
226
224
 
227
225
  ##
@@ -289,14 +287,13 @@ module CloudstackClient
289
287
  # Destroy the server with the specified name.
290
288
  #
291
289
 
292
- def destroy_server(id)
290
+ def destroy_server(id, async = true)
293
291
  params = {
294
292
  'command' => 'destroyVirtualMachine',
295
293
  'id' => id
296
294
  }
297
295
 
298
- json = send_async_request(params)
299
- json['virtualmachine']
296
+ async ? send_async_request(params)['virtualmachine'] : send_request(params)
300
297
  end
301
298
 
302
299
  end
@@ -64,6 +64,7 @@ module CloudstackClient
64
64
  params['domainid'] = account["domainid"]
65
65
  params['account'] = args[:account]
66
66
  end
67
+ params['publickey'] = args[:public_key] if args[:public_key]
67
68
 
68
69
  json = send_request(params)['keypair']
69
70
  end
@@ -1,3 +1,3 @@
1
1
  module CloudstackClient
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudstack-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-20 00:00:00.000000000 Z
12
+ date: 2013-08-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdoc