cloudstack-cli 0.3.4 → 0.3.5

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.
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