cloudstack-cli 0.0.1

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.
@@ -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,9 @@
1
+ class Publicip < Thor
2
+
3
+ desc "remove ID", "remove public IP address"
4
+ def remove(id)
5
+ cs_cli = CloudstackCli::Helper.new
6
+ puts "OK" if cs_cli.remove_publicip(id)
7
+ end
8
+
9
+ 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