cloudstack-cli 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +7 -0
- data/README.md +40 -0
- data/bin/cs +7 -0
- data/cloudstack-cli.gemspec +25 -0
- data/config/cloudstack.example.yml +3 -0
- data/lib/cloudstack-cli/cli.rb +59 -0
- data/lib/cloudstack-cli/commands/account.rb +16 -0
- data/lib/cloudstack-cli/commands/domain.rb +16 -0
- data/lib/cloudstack-cli/commands/load_balancer.rb +58 -0
- data/lib/cloudstack-cli/commands/network.rb +74 -0
- data/lib/cloudstack-cli/commands/offering.rb +80 -0
- data/lib/cloudstack-cli/commands/project.rb +15 -0
- data/lib/cloudstack-cli/commands/publicip.rb +9 -0
- data/lib/cloudstack-cli/commands/router.rb +94 -0
- data/lib/cloudstack-cli/commands/server.rb +65 -0
- data/lib/cloudstack-cli/commands/stack.rb +16 -0
- data/lib/cloudstack-cli/commands/template.rb +25 -0
- data/lib/cloudstack-cli/commands/volume.rb +20 -0
- data/lib/cloudstack-cli/commands/zone.rb +16 -0
- data/lib/cloudstack-cli/helper.rb +236 -0
- data/lib/cloudstack-cli/version.rb +3 -0
- data/lib/cloudstack-client/client.rb +871 -0
- data/lib/cloudstack-client/helper.rb +12 -0
- data/lib/cloudstack-client/ssh_command.rb +15 -0
- data/lib/cloudstack_cli.rb +16 -0
- data/lib/cloudstack_client.rb +5 -0
- metadata +171 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
class Project < Thor
|
2
|
+
|
3
|
+
desc "list", "list projects"
|
4
|
+
def list
|
5
|
+
cs_cli = CloudstackCli::Helper.new
|
6
|
+
projects = cs_cli.projects
|
7
|
+
if projects.size < 1
|
8
|
+
puts "No projects found"
|
9
|
+
else
|
10
|
+
projects.each do |project|
|
11
|
+
puts "#{project['name']} - #{project['displaytext']} - #{project['domain']}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class Router < Thor
|
2
|
+
include CommandLineReporter
|
3
|
+
|
4
|
+
desc "list", "list virtual routers"
|
5
|
+
option :project
|
6
|
+
option :account
|
7
|
+
option :status, desc: "Running or Stopped"
|
8
|
+
option :redundant_state, desc: "MASTER, BACKUP or UNKNOWN"
|
9
|
+
option :listall, type: :boolean
|
10
|
+
option :text, type: :boolean, desc: "text output (only the instance name)"
|
11
|
+
def list
|
12
|
+
cs_cli = CloudstackCli::Helper.new
|
13
|
+
if options[:project]
|
14
|
+
project = cs_cli.projects.select { |p| p['name'] == options[:project] }.first
|
15
|
+
raise "Project '#{options[:project]}' not found" unless project
|
16
|
+
projectid = project['id']
|
17
|
+
end
|
18
|
+
|
19
|
+
routers = cs_cli.list_routers(
|
20
|
+
{
|
21
|
+
account: options[:account],
|
22
|
+
projectid: projectid,
|
23
|
+
status: options[:status],
|
24
|
+
},
|
25
|
+
options[:redundant_state]
|
26
|
+
)
|
27
|
+
|
28
|
+
|
29
|
+
if options[:listall]
|
30
|
+
projects = cs_cli.projects
|
31
|
+
projects.each do |project|
|
32
|
+
routers = routers + cs_cli.list_routers(
|
33
|
+
{
|
34
|
+
account: options[:account],
|
35
|
+
projectid: project['id'],
|
36
|
+
status: options[:status]
|
37
|
+
},
|
38
|
+
options[:redundant_state]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if options[:text]
|
44
|
+
puts routers.map {|r| r['name']}.join(" ")
|
45
|
+
else
|
46
|
+
puts "Total number of routers: #{routers.size}"
|
47
|
+
table(border: true) do
|
48
|
+
row do
|
49
|
+
column 'ID', width: 40
|
50
|
+
column 'Name'
|
51
|
+
column 'Zone'
|
52
|
+
column 'Account', width: 14 unless options[:project]
|
53
|
+
column 'Project', width: 14 if options[:listall] || options[:project]
|
54
|
+
column 'Redundant State'
|
55
|
+
column 'Linklocal IP', width: 15
|
56
|
+
column 'Status'
|
57
|
+
end
|
58
|
+
routers.each do |router|
|
59
|
+
row do
|
60
|
+
column router["id"]
|
61
|
+
column router["name"]
|
62
|
+
column router["zonename"]
|
63
|
+
column router["account"] unless options[:project]
|
64
|
+
column router["project"] if options[:listall] || options[:project]
|
65
|
+
column router["redundantstate"]
|
66
|
+
column router["linklocalip"]
|
67
|
+
column router["state"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "stop NAME", "stop virtual router"
|
76
|
+
option :project
|
77
|
+
|
78
|
+
def stopall
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "start NAME", "start virtual router"
|
83
|
+
option :project
|
84
|
+
def start
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "destroy ID", "destroy virtual router"
|
89
|
+
def destroy(id)
|
90
|
+
cs_cli = CloudstackCli::Helper.new
|
91
|
+
puts "OK" if cs_cli.destroy_router(name)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Server < Thor
|
2
|
+
|
3
|
+
desc "list", "list servers"
|
4
|
+
option :listall, :type => :boolean
|
5
|
+
option :text, :type => :boolean
|
6
|
+
option :project
|
7
|
+
option :account
|
8
|
+
def list
|
9
|
+
cs_cli = CloudstackCli::Helper.new
|
10
|
+
if options[:project]
|
11
|
+
project = cs_cli.projects.select { |p| p['name'] == options[:project] }.first
|
12
|
+
exit_now! "Project '#{options[:project]}' not found" unless project
|
13
|
+
options[:project_id] = project['id']
|
14
|
+
options[:account] = nil
|
15
|
+
end
|
16
|
+
servers = cs_cli.virtual_machines(options)
|
17
|
+
if servers.size < 1
|
18
|
+
puts "No servers found"
|
19
|
+
else
|
20
|
+
if options[:text]
|
21
|
+
servers.each do |server|
|
22
|
+
puts "#{server['name']} - #{server['state']} - #{server['domain']}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
cs_cli.virtual_machines_table(servers)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "create NAME", "create a server"
|
31
|
+
option :zone, :required => true
|
32
|
+
option :template, :required => true
|
33
|
+
option :offering, :required => true
|
34
|
+
option :networks, :type => :array, :required => true
|
35
|
+
option :project
|
36
|
+
option :port_forwarding, :type => :array, :aliases => :pf, :default => [], :description => "public_ip:port"
|
37
|
+
option :interactive, :type => :boolean
|
38
|
+
def create(name)
|
39
|
+
CloudstackCli::Cli.new.bootstrap_server(
|
40
|
+
name,
|
41
|
+
options[:zone],
|
42
|
+
options[:template],
|
43
|
+
options[:offering],
|
44
|
+
options[:networks],
|
45
|
+
options[:port_forwarding],
|
46
|
+
options[:project]
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "stop NAME", "stop a server"
|
51
|
+
def stop(name)
|
52
|
+
CloudstackCli::Cli.new.stop_server(name)
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "start NAME", "start a server"
|
56
|
+
def start(name)
|
57
|
+
CloudstackCli::Cli.new.start_server(name)
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "reboot NAME", "reboot a server"
|
61
|
+
def restart(name)
|
62
|
+
CloudstackCli::Cli.new.reboot_server(name)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Stack < Thor
|
2
|
+
|
3
|
+
desc "create STACKFILE", "create a stack of servers"
|
4
|
+
def create(stackfile)
|
5
|
+
CloudstackCli::Cli.new.bootstrap_server(
|
6
|
+
name,
|
7
|
+
options[:zone],
|
8
|
+
options[:template],
|
9
|
+
options[:offering],
|
10
|
+
options[:networks],
|
11
|
+
options[:port_forwarding],
|
12
|
+
options[:project]
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Template < Thor
|
2
|
+
|
3
|
+
desc 'list', 'list templates by type [featured|self|self-executable|executable|community]'
|
4
|
+
option :project
|
5
|
+
def list(type='featured')
|
6
|
+
cs_cli = CloudstackCli::Helper.new
|
7
|
+
|
8
|
+
if options[:project]
|
9
|
+
project = cs_cli.projects.select { |p| p['name'] == options[:project] }.first
|
10
|
+
exit_now! "Project '#{options[:project]}' not found" unless project
|
11
|
+
end
|
12
|
+
|
13
|
+
exit_now! "unsupported template type '#{type}'" unless
|
14
|
+
%w(featured self self-executable executable community).include? type
|
15
|
+
templates = cs_cli.templates(type, project ? project['id'] : nil)
|
16
|
+
if templates.size < 1
|
17
|
+
puts "No templates found"
|
18
|
+
else
|
19
|
+
templates.each do |template|
|
20
|
+
puts template['name']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Volume < Thor
|
2
|
+
desc "list", "list networks"
|
3
|
+
option :project
|
4
|
+
def list
|
5
|
+
cs_cli = CloudstackCli::Helper.new
|
6
|
+
if options[:project]
|
7
|
+
project = cs_cli.projects.select { |p| p['name'] == options[:project] }.first
|
8
|
+
raise "Project '#{options[:project]}' not found" unless project
|
9
|
+
end
|
10
|
+
|
11
|
+
networks = cs_cli.networks(project ? project['id'] : nil)
|
12
|
+
if networks.size < 1
|
13
|
+
puts "No networks found"
|
14
|
+
else
|
15
|
+
networks.each do |network|
|
16
|
+
puts "#{network['name']} - #{network['displaytext']} #{' - Default' if network['isdefault']}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Zone < Thor
|
2
|
+
|
3
|
+
desc "list", "list zones"
|
4
|
+
def list
|
5
|
+
cs_cli = CloudstackCli::Helper.new
|
6
|
+
zones = cs_cli.zones
|
7
|
+
if zones.size < 1
|
8
|
+
puts "No projects found"
|
9
|
+
else
|
10
|
+
zones.each do |zone|
|
11
|
+
puts "#{zone['name']} - #{zone['description']}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
module CloudstackCli
|
2
|
+
class Helper
|
3
|
+
include CommandLineReporter
|
4
|
+
|
5
|
+
attr_reader :cs
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@cs = CloudstackClient::Connection.new(
|
9
|
+
options[:cloudstack_url],
|
10
|
+
options[:cloudstack_api_key],
|
11
|
+
options[:cloudstack_secret_key]
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_publicip(id)
|
16
|
+
@cs.disassociate_ip_address(id)
|
17
|
+
end
|
18
|
+
|
19
|
+
def domains(name = nil)
|
20
|
+
@cs.list_domains(name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def server_offerings(domain = nil)
|
24
|
+
@server_offerings ||= @cs.list_service_offerings(domain)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_offering(params)
|
28
|
+
@cs.create_offering(params)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_offering(id)
|
32
|
+
@cs.delete_offering(id)
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_offering(args)
|
36
|
+
@cs.update_offering(args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def templates(type = 'featured', project_id)
|
40
|
+
@templates ||= @cs.list_templates(type, project_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def projects
|
44
|
+
@projects ||= @cs.list_projects
|
45
|
+
end
|
46
|
+
|
47
|
+
def zones
|
48
|
+
@zones ||= @cs.list_zones
|
49
|
+
end
|
50
|
+
|
51
|
+
def networks(project_id = nil)
|
52
|
+
@cs.list_networks(project_id)
|
53
|
+
end
|
54
|
+
|
55
|
+
def physical_networks
|
56
|
+
@cs.list_physical_networks
|
57
|
+
end
|
58
|
+
|
59
|
+
def volumes(project_id = nil)
|
60
|
+
@cs.list_volumes(project_id)
|
61
|
+
end
|
62
|
+
|
63
|
+
def virtual_machines(options = {})
|
64
|
+
@cs.list_servers(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def virtual_machines_table(vms)
|
68
|
+
table(border: true) do
|
69
|
+
row do
|
70
|
+
column 'Name', width: 20
|
71
|
+
column 'State'
|
72
|
+
column 'Offering', align: 'right'
|
73
|
+
column 'Template', align: 'right', width: 30
|
74
|
+
column 'IP\'s', width: 14
|
75
|
+
column 'Project'
|
76
|
+
column 'Account'
|
77
|
+
end
|
78
|
+
vms.each do |vm|
|
79
|
+
row do
|
80
|
+
column vm['name']
|
81
|
+
column vm['state']
|
82
|
+
column vm['serviceofferingname']
|
83
|
+
column vm['templatename']
|
84
|
+
column vm['nic'].map { |nic| nic['ipaddress']}.join(" ")
|
85
|
+
column vm['project']
|
86
|
+
column vm['account']
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def bootstrap_server(name, zone, template, offering, networks, pf_rules = [], project = nil)
|
93
|
+
puts "Create server #{name}...".color(:yellow)
|
94
|
+
server = @cs.create_server(
|
95
|
+
name,
|
96
|
+
offering,
|
97
|
+
template,
|
98
|
+
zone,
|
99
|
+
networks,
|
100
|
+
project
|
101
|
+
)
|
102
|
+
|
103
|
+
puts
|
104
|
+
puts "Server #{server["name"]} has been created.".color(:green)
|
105
|
+
puts
|
106
|
+
puts "Make sure the server is running...".color(:yellow)
|
107
|
+
@cs.wait_for_server_state(server["id"], "Running")
|
108
|
+
puts "OK!".color(:green)
|
109
|
+
puts
|
110
|
+
puts "Get the fqdn of the server...".color(:yellow)
|
111
|
+
server_fqdn = @cs.get_server_fqdn(server)
|
112
|
+
puts "FQDN is #{server_fqdn}".color(:green)
|
113
|
+
|
114
|
+
if pf_rules.size > 0
|
115
|
+
puts
|
116
|
+
pf_rules.each do |pf_rule|
|
117
|
+
ip = pf_rule.split(":")[0]
|
118
|
+
ip_addr = @cs.get_public_ip_address(ip)
|
119
|
+
port = pf_rule.split(":")[1]
|
120
|
+
print "Create port forwarding rule #{ip}:#{port} ".color(:yellow)
|
121
|
+
@cs.create_port_forwarding_rule(ip_addr["id"], port, 'TCP', port, server["id"])
|
122
|
+
puts
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
puts
|
127
|
+
puts "Complete!".color(:green)
|
128
|
+
end
|
129
|
+
|
130
|
+
def stop_server(name)
|
131
|
+
@cs.stop_server(name)
|
132
|
+
end
|
133
|
+
|
134
|
+
def start_server(name)
|
135
|
+
@cs.start_server(name)
|
136
|
+
end
|
137
|
+
|
138
|
+
def reboot_server(name)
|
139
|
+
@cs.reboot_server(name)
|
140
|
+
end
|
141
|
+
|
142
|
+
def list_accounts(name = nil)
|
143
|
+
@cs.list_accounts({ name: name })
|
144
|
+
end
|
145
|
+
|
146
|
+
def list_load_balancer_rules(project = nil)
|
147
|
+
@cs.list_load_balancer_rules(project)
|
148
|
+
end
|
149
|
+
|
150
|
+
def create_load_balancer_rule(name, ip, private_port, public_port, options = {})
|
151
|
+
puts "Create rule #{name}...".color(:yellow)
|
152
|
+
@cs.create_load_balancer_rule(name, ip, private_port, public_port, options = {})
|
153
|
+
puts "OK!".color(:green)
|
154
|
+
end
|
155
|
+
|
156
|
+
def assign_to_load_balancer_rule(id, names)
|
157
|
+
puts "Add #{names.join(', ')} to rule #{id}...".color(:yellow)
|
158
|
+
rule = @cs.assign_to_load_balancer_rule(id, names)
|
159
|
+
if rule['success']
|
160
|
+
puts "OK!".color(:green)
|
161
|
+
else
|
162
|
+
puts "Failed!".color(:red)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def list_routers(args, redundant_state = nil)
|
167
|
+
routers = @cs.list_routers(args)
|
168
|
+
if redundant_state
|
169
|
+
return routers.select {|r| r['redundantstate'].downcase == redundant_state.downcase }
|
170
|
+
end
|
171
|
+
routers
|
172
|
+
end
|
173
|
+
|
174
|
+
def destroy_router(id)
|
175
|
+
@cs.destroy_router(id)
|
176
|
+
end
|
177
|
+
|
178
|
+
def options
|
179
|
+
@options ||= CloudstackClient::ConnectionHelper.load_configuration()
|
180
|
+
end
|
181
|
+
|
182
|
+
def print_options(options, attr = 'name')
|
183
|
+
options.to_enum.with_index(1).each do |option, i|
|
184
|
+
puts "#{i}: #{option[attr]}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def interactive
|
189
|
+
ARGV.clear
|
190
|
+
puts
|
191
|
+
puts %{We are going to deploy a new server and...
|
192
|
+
- assign a public IP address
|
193
|
+
- create a firewall rule for SSH and HTTP access
|
194
|
+
- connect to the server and install the puppet client}.color(:magenta)
|
195
|
+
puts
|
196
|
+
|
197
|
+
print "Please provide a name for the new server".background(:blue)
|
198
|
+
puts " (spaces or special characters are NOT allowed): "
|
199
|
+
server_name = gets.chomp
|
200
|
+
|
201
|
+
if projects.size > 0
|
202
|
+
puts "Select a project".background(:blue)
|
203
|
+
print_options(projects)
|
204
|
+
project = gets.chomp.to_i - 1
|
205
|
+
end
|
206
|
+
|
207
|
+
puts "Select a computing offering:".background(:blue)
|
208
|
+
print_options(server_offerings)
|
209
|
+
service_offering = gets.chomp.to_i - 1
|
210
|
+
|
211
|
+
puts "Select a template:".background(:blue)
|
212
|
+
print_options(templates)
|
213
|
+
template = gets.chomp.to_i - 1
|
214
|
+
|
215
|
+
puts "Select a availability zone:".background(:blue)
|
216
|
+
print_options(zones)
|
217
|
+
zone = gets.chomp.to_i - 1
|
218
|
+
|
219
|
+
# FIXME: show only networks in selected zone
|
220
|
+
puts "Select a network:".background(:blue)
|
221
|
+
project_id = projects[project]['id'] rescue nil
|
222
|
+
networks = @cs.list_networks(project_id)
|
223
|
+
print_options(networks)
|
224
|
+
network = gets.chomp.to_i - 1
|
225
|
+
|
226
|
+
bootstrap_server(
|
227
|
+
server_name,
|
228
|
+
zones[zone]["name"],
|
229
|
+
templates[template]["name"],
|
230
|
+
server_offerings[service_offering]["name"],
|
231
|
+
[networks[network]["name"]],
|
232
|
+
projects[project]["name"]
|
233
|
+
)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|