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 +1 -1
- data/lib/cloudstack-cli/commands/capacity.rb +26 -13
- data/lib/cloudstack-cli/commands/network.rb +2 -1
- data/lib/cloudstack-cli/commands/port_rule.rb +4 -0
- data/lib/cloudstack-cli/commands/server.rb +36 -2
- data/lib/cloudstack-cli/commands/ssh_key_pairs.rb +1 -0
- data/lib/cloudstack-cli/commands/stack.rb +77 -28
- data/lib/cloudstack-cli/helper.rb +56 -21
- data/lib/cloudstack-cli/version.rb +1 -1
- data/lib/cloudstack-client/client.rb +3 -1
- data/lib/cloudstack-client/commands/port_forwarding_rule.rb +2 -3
- data/lib/cloudstack-client/commands/server.rb +3 -6
- data/lib/cloudstack-client/commands/ssh_key_pair.rb +1 -0
- data/lib/cloudstack-client/version.rb +1 -1
- metadata +2 -2
data/Gemfile.lock
CHANGED
@@ -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
|
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
|
26
|
-
c
|
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(
|
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(
|
51
|
-
|
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)"
|
@@ -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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
42
|
-
server["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(',
|
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
|
-
|
32
|
-
while status.
|
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 {
|
27
|
+
t = Thread.new { jobs = update_job_status(jobs) }
|
35
28
|
while t.alive?
|
36
|
-
chars = print_job_status(jobs,
|
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,
|
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,
|
42
|
+
print_job_status(jobs, chars,
|
43
|
+
call == 0 ? opts.merge(no_clear: true) : opts
|
44
|
+
)
|
47
45
|
end
|
48
46
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
97
|
-
|
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
|
@@ -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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
299
|
-
json['virtualmachine']
|
296
|
+
async ? send_async_request(params)['virtualmachine'] : send_request(params)
|
300
297
|
end
|
301
298
|
|
302
299
|
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
|
+
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-
|
12
|
+
date: 2013-08-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rdoc
|