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