knife-cosmic 0.2.0
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.
- checksums.yaml +7 -0
- data/CHANGES.rdoc +186 -0
- data/LICENSE +202 -0
- data/README.rdoc +427 -0
- data/lib/chef/knife/cosmic_aag_list.rb +58 -0
- data/lib/chef/knife/cosmic_account_list.rb +87 -0
- data/lib/chef/knife/cosmic_base.rb +108 -0
- data/lib/chef/knife/cosmic_baselist.rb +111 -0
- data/lib/chef/knife/cosmic_cluster_list.rb +60 -0
- data/lib/chef/knife/cosmic_config_list.rb +56 -0
- data/lib/chef/knife/cosmic_disk_list.rb +58 -0
- data/lib/chef/knife/cosmic_domain_list.rb +53 -0
- data/lib/chef/knife/cosmic_firewallrule_create.rb +138 -0
- data/lib/chef/knife/cosmic_firewallrule_list.rb +62 -0
- data/lib/chef/knife/cosmic_forwardrule_create.rb +145 -0
- data/lib/chef/knife/cosmic_host_list.rb +61 -0
- data/lib/chef/knife/cosmic_hosts.rb +58 -0
- data/lib/chef/knife/cosmic_iso_list.rb +89 -0
- data/lib/chef/knife/cosmic_keypair_create.rb +72 -0
- data/lib/chef/knife/cosmic_keypair_delete.rb +60 -0
- data/lib/chef/knife/cosmic_keypair_list.rb +44 -0
- data/lib/chef/knife/cosmic_network_list.rb +63 -0
- data/lib/chef/knife/cosmic_oscategory_list.rb +50 -0
- data/lib/chef/knife/cosmic_ostype_list.rb +52 -0
- data/lib/chef/knife/cosmic_pod_list.rb +60 -0
- data/lib/chef/knife/cosmic_project_list.rb +63 -0
- data/lib/chef/knife/cosmic_publicip_list.rb +55 -0
- data/lib/chef/knife/cosmic_router_list.rb +64 -0
- data/lib/chef/knife/cosmic_securitygroup_list.rb +59 -0
- data/lib/chef/knife/cosmic_server_add_nic.rb +109 -0
- data/lib/chef/knife/cosmic_server_create.rb +674 -0
- data/lib/chef/knife/cosmic_server_delete.rb +153 -0
- data/lib/chef/knife/cosmic_server_list.rb +167 -0
- data/lib/chef/knife/cosmic_server_passwordreset.rb +91 -0
- data/lib/chef/knife/cosmic_server_reboot.rb +99 -0
- data/lib/chef/knife/cosmic_server_remove_nic.rb +101 -0
- data/lib/chef/knife/cosmic_server_start.rb +104 -0
- data/lib/chef/knife/cosmic_server_stop.rb +118 -0
- data/lib/chef/knife/cosmic_server_update.rb +47 -0
- data/lib/chef/knife/cosmic_service_list.rb +74 -0
- data/lib/chef/knife/cosmic_stack_create.rb +298 -0
- data/lib/chef/knife/cosmic_stack_delete.rb +79 -0
- data/lib/chef/knife/cosmic_template_create.rb +129 -0
- data/lib/chef/knife/cosmic_template_extract.rb +104 -0
- data/lib/chef/knife/cosmic_template_list.rb +88 -0
- data/lib/chef/knife/cosmic_template_register.rb +187 -0
- data/lib/chef/knife/cosmic_user_list.rb +62 -0
- data/lib/chef/knife/cosmic_volume_attach.rb +70 -0
- data/lib/chef/knife/cosmic_volume_create.rb +108 -0
- data/lib/chef/knife/cosmic_volume_delete.rb +97 -0
- data/lib/chef/knife/cosmic_volume_detach.rb +61 -0
- data/lib/chef/knife/cosmic_volume_list.rb +77 -0
- data/lib/chef/knife/cosmic_zone_list.rb +53 -0
- data/lib/knife-cosmic/connection.rb +1046 -0
- metadata +127 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-cloudstack author:: Ryan Holmes (<rholmes@edmunds.com>)
|
3
|
+
# Original knife-cloudstack author:: Sander Botman (<sbotman@schubergphilis.com>)
|
4
|
+
# Copyright:: Copyright (c) 2011 Edmunds, Inc.
|
5
|
+
# Copyright:: Copyright (c) 2013 Sander Botman.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'chef/knife'
|
22
|
+
require 'chef/knife/cosmic_baselist'
|
23
|
+
|
24
|
+
module Knifecosmic
|
25
|
+
class CosmicZoneList < Chef::Knife
|
26
|
+
|
27
|
+
include Chef::Knife::KnifecosmicBaseList
|
28
|
+
|
29
|
+
banner "knife cosmic zone list (options)"
|
30
|
+
|
31
|
+
option :keyword,
|
32
|
+
:long => "--keyword KEY",
|
33
|
+
:description => "List by keyword"
|
34
|
+
|
35
|
+
def run
|
36
|
+
validate_base_options
|
37
|
+
|
38
|
+
columns = [
|
39
|
+
'Name :name',
|
40
|
+
'Network Type :networktype',
|
41
|
+
'Security Groups :securitygroupsenabled'
|
42
|
+
]
|
43
|
+
|
44
|
+
params = { 'command' => "listZones" }
|
45
|
+
params['filter'] = locate_config_value(:filter) if locate_config_value(:filter)
|
46
|
+
params['keyword'] = locate_config_value(:keyword) if locate_config_value(:keyword)
|
47
|
+
|
48
|
+
result = connection.list_object(params, "zone")
|
49
|
+
list_object(columns, result)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,1046 @@
|
|
1
|
+
#
|
2
|
+
# knife-cosmic author:: Robbert-Jan Sperna Weiland (<rspernaweiland@schubergphilis.com>)
|
3
|
+
# Original knife-cloudstack author:: Ryan Holmes (<rholmes@edmunds.com>)
|
4
|
+
# Original knife-cloudstack author:: KC Braunschweig (<kcbraunschweig@gmail.com>)
|
5
|
+
# Original knife-cloudstack author:: Sander Botman (<sbotman@schubergphilis.com>)
|
6
|
+
# Original knife-cloudstack author:: Frank Breedijk (<fbreedijk@schubergphilis.com>)
|
7
|
+
# Original knife-cloudstack author:: Sander van Harmelen (<svanharmelen@schubergphilis.com>)
|
8
|
+
# Copyright:: Copyright (c) 2011 Edmunds, Inc.
|
9
|
+
# License:: Apache License, Version 2.0
|
10
|
+
#
|
11
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
12
|
+
# you may not use this file except in compliance with the License.
|
13
|
+
# You may obtain a copy of the License at
|
14
|
+
#
|
15
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
16
|
+
#
|
17
|
+
# Unless required by applicable law or agreed to in writing, software
|
18
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
19
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
20
|
+
# See the License for the specific language governing permissions and
|
21
|
+
# limitations under the License.
|
22
|
+
#
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'base64'
|
26
|
+
require 'openssl'
|
27
|
+
require 'uri'
|
28
|
+
require 'cgi'
|
29
|
+
require 'net/http'
|
30
|
+
require 'json'
|
31
|
+
require 'highline/import'
|
32
|
+
|
33
|
+
class String
|
34
|
+
def to_regexp
|
35
|
+
return nil unless self.strip.match(/\A\/(.*)\/(.*)\Z/mx)
|
36
|
+
regexp , flags = $1 , $2
|
37
|
+
return nil if !regexp || flags =~ /[^xim]/m
|
38
|
+
|
39
|
+
x = /x/.match(flags) && Regexp::EXTENDED
|
40
|
+
i = /i/.match(flags) && Regexp::IGNORECASE
|
41
|
+
m = /m/.match(flags) && Regexp::MULTILINE
|
42
|
+
|
43
|
+
Regexp.new regexp , [x,i,m].inject(0){|a,f| f ? a+f : a }
|
44
|
+
end
|
45
|
+
|
46
|
+
def is_uuid?
|
47
|
+
self.strip =~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/ ? true : false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module CosmicClient
|
52
|
+
class Connection
|
53
|
+
|
54
|
+
ASYNC_POLL_INTERVAL = 5.0
|
55
|
+
ASYNC_TIMEOUT = 3600
|
56
|
+
|
57
|
+
def initialize(api_url, api_key, secret_key, project_name=nil, no_ssl_verify=false, api_proxy=nil)
|
58
|
+
@api_url = api_url
|
59
|
+
@api_key = api_key
|
60
|
+
@secret_key = secret_key
|
61
|
+
@no_ssl_verify = no_ssl_verify
|
62
|
+
@project_id = get_project_id(project_name) if project_name || nil
|
63
|
+
@api_proxy = api_proxy
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Get project id
|
68
|
+
def get_project_id(name)
|
69
|
+
project = get_project(name)
|
70
|
+
if !project then
|
71
|
+
puts "Project #{project_name} does not exist"
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
project['id']
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Finds the server with the specified name.
|
79
|
+
|
80
|
+
def get_server(name)
|
81
|
+
params = {
|
82
|
+
'command' => 'listVirtualMachines',
|
83
|
+
'name' => name
|
84
|
+
}
|
85
|
+
json = send_request(params)
|
86
|
+
machines = json['virtualmachine']
|
87
|
+
|
88
|
+
if !machines || machines.empty? then
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
machine = machines.select { |item| name == item['name'] }
|
92
|
+
machine.first
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Finds the public ip for a server
|
97
|
+
|
98
|
+
def get_server_public_ip(server, cached_rules=nil, cached_nat=nil)
|
99
|
+
return nil unless server
|
100
|
+
# find the public ip
|
101
|
+
nic = get_server_default_nic(server)
|
102
|
+
|
103
|
+
ssh_rule = get_ssh_port_forwarding_rule(server, cached_rules)
|
104
|
+
return ssh_rule['ipaddress'] if ssh_rule
|
105
|
+
|
106
|
+
winrm_rule = get_winrm_port_forwarding_rule(server, cached_rules)
|
107
|
+
return winrm_rule['ipaddress'] if winrm_rule
|
108
|
+
|
109
|
+
#check for static NAT
|
110
|
+
if cached_nat
|
111
|
+
ip_addr = cached_nat.find {|v| v['virtualmachineid'] == server['id']}
|
112
|
+
else
|
113
|
+
ip_addr = list_public_ip_addresses.find {|v| v['virtualmachineid'] == server['id']}
|
114
|
+
end
|
115
|
+
if ip_addr
|
116
|
+
return ip_addr['ipaddress']
|
117
|
+
end
|
118
|
+
if nic.empty?
|
119
|
+
return []
|
120
|
+
end
|
121
|
+
nic['ipaddress'] || []
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Returns the fully qualified domain name for a server.
|
126
|
+
|
127
|
+
def get_server_fqdn(server)
|
128
|
+
return nil unless server
|
129
|
+
|
130
|
+
nic = get_server_default_nic(server) || {}
|
131
|
+
networks = list_networks || {}
|
132
|
+
|
133
|
+
id = nic['networkid']
|
134
|
+
network = networks.select { |net|
|
135
|
+
net['id'] == id
|
136
|
+
}.first
|
137
|
+
return nil unless network
|
138
|
+
|
139
|
+
"#{server['name']}.#{network['networkdomain']}"
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_server_default_nic(server)
|
143
|
+
server['nic'].each do |nic|
|
144
|
+
return nic if nic['isdefault']
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# List all the objects based on the command that is specified.
|
150
|
+
|
151
|
+
def list_object(params, json_result)
|
152
|
+
json = send_request(params)
|
153
|
+
Chef::Log.debug("JSON (list_object) result: #{json}")
|
154
|
+
|
155
|
+
result = json["#{json_result}"] || []
|
156
|
+
result = data_filter(result, params['filter']) if params['filter']
|
157
|
+
result
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Lists all the servers in your account.
|
162
|
+
|
163
|
+
def list_servers
|
164
|
+
params = {
|
165
|
+
'command' => 'listVirtualMachines'
|
166
|
+
}
|
167
|
+
json = send_request(params)
|
168
|
+
json['virtualmachine'] || []
|
169
|
+
end
|
170
|
+
|
171
|
+
## Updates a server
|
172
|
+
def server_update(params)
|
173
|
+
params['command'] = 'updateVirtualMachine'
|
174
|
+
|
175
|
+
json = send_request(params)
|
176
|
+
json
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
##
|
181
|
+
# Deploys a new server using the specified parameters.
|
182
|
+
|
183
|
+
def create_server(host_name, service_name, template_name, disk_name=nil, zone_name=nil, network_names=[], extra_params)
|
184
|
+
|
185
|
+
if host_name then
|
186
|
+
if get_server(host_name) then
|
187
|
+
puts "\nError: Server '#{host_name}' already exists."
|
188
|
+
exit 1
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
service = get_service_offering(service_name)
|
193
|
+
if !service then
|
194
|
+
puts "\nError: Service offering '#{service_name}' is invalid"
|
195
|
+
exit 1
|
196
|
+
end
|
197
|
+
|
198
|
+
template = get_template(template_name, zone_name)
|
199
|
+
template = get_iso(template_name, zone_name) unless template
|
200
|
+
|
201
|
+
if !template then
|
202
|
+
puts "\nError: Template / ISO name: '#{template_name}' is invalid"
|
203
|
+
exit 1
|
204
|
+
end
|
205
|
+
|
206
|
+
if disk_name then
|
207
|
+
disk = get_disk_offering(disk_name)
|
208
|
+
if !disk then
|
209
|
+
puts "\nError: Disk offering '#{disk_name}' is invalid"
|
210
|
+
exit 1
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
zone = zone_name ? get_zone(zone_name) : get_default_zone
|
215
|
+
if !zone then
|
216
|
+
msg = zone_name ? "Zone '#{zone_name}' is invalid" : "No default zone found"
|
217
|
+
puts "\nError: #{msg}"
|
218
|
+
exit 1
|
219
|
+
end
|
220
|
+
|
221
|
+
if zone['networktype'] != 'Basic' then
|
222
|
+
# If this is a Basic zone no networkids are needed in the params
|
223
|
+
|
224
|
+
networks = []
|
225
|
+
if network_names.nil? then
|
226
|
+
networks << get_default_network(zone['id'])
|
227
|
+
else
|
228
|
+
network_names.each do |name|
|
229
|
+
network = get_network(name)
|
230
|
+
if !network then
|
231
|
+
puts "\nError: Network '#{name}' not found"
|
232
|
+
end
|
233
|
+
networks << get_network(name)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
if networks.empty? then
|
238
|
+
networks << get_default_network(zone['id'])
|
239
|
+
end
|
240
|
+
if networks.empty? then
|
241
|
+
puts "\nError: No default network found"
|
242
|
+
exit 1
|
243
|
+
end
|
244
|
+
network_ids = networks.map { |network|
|
245
|
+
network['id']
|
246
|
+
}
|
247
|
+
|
248
|
+
params = {
|
249
|
+
'command' => 'deployVirtualMachine',
|
250
|
+
'serviceOfferingId' => service['id'],
|
251
|
+
'templateId' => template['id'],
|
252
|
+
'zoneId' => zone['id'],
|
253
|
+
'networkids' => network_ids.join(',')
|
254
|
+
}
|
255
|
+
|
256
|
+
else
|
257
|
+
|
258
|
+
params = {
|
259
|
+
'command' => 'deployVirtualMachine',
|
260
|
+
'serviceOfferingId' => service['id'],
|
261
|
+
'templateId' => template['id'],
|
262
|
+
'zoneId' => zone['id']
|
263
|
+
}
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
params.merge!(extra_params) if extra_params
|
268
|
+
|
269
|
+
params['name'] = host_name if host_name
|
270
|
+
params['diskOfferingId'] = disk['id'] if disk
|
271
|
+
|
272
|
+
json = send_async_request(params)
|
273
|
+
json['virtualmachine']
|
274
|
+
end
|
275
|
+
|
276
|
+
##
|
277
|
+
# Deletes the server with the specified name.
|
278
|
+
#
|
279
|
+
|
280
|
+
def delete_server(name, expunge)
|
281
|
+
server = get_server(name)
|
282
|
+
if !server || !server['id'] then
|
283
|
+
puts "\nError: Virtual machine '#{name}' does not exist"
|
284
|
+
exit 1
|
285
|
+
end
|
286
|
+
|
287
|
+
params = {
|
288
|
+
'command' => 'destroyVirtualMachine',
|
289
|
+
'id' => server['id'],
|
290
|
+
'expunge' => expunge
|
291
|
+
}
|
292
|
+
|
293
|
+
json = send_async_request(params)
|
294
|
+
json['virtualmachine']
|
295
|
+
end
|
296
|
+
|
297
|
+
##
|
298
|
+
# Stops the server with the specified name.
|
299
|
+
#
|
300
|
+
|
301
|
+
def stop_server(name, forced=nil)
|
302
|
+
server = get_server(name)
|
303
|
+
if !server || !server['id'] then
|
304
|
+
puts "\nError: Virtual machine '#{name}' does not exist"
|
305
|
+
exit 1
|
306
|
+
end
|
307
|
+
|
308
|
+
params = {
|
309
|
+
'command' => 'stopVirtualMachine',
|
310
|
+
'id' => server['id']
|
311
|
+
}
|
312
|
+
params['forced'] = true if forced
|
313
|
+
|
314
|
+
json = send_async_request(params)
|
315
|
+
json['virtualmachine']
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# Resets root password for server with the specified name.
|
320
|
+
#
|
321
|
+
|
322
|
+
def server_passwordreset(name)
|
323
|
+
server = get_server(name)
|
324
|
+
if !server || !server['id'] then
|
325
|
+
puts "\nError: Virtual machine '#{name}' does not exist"
|
326
|
+
exit 1
|
327
|
+
end
|
328
|
+
|
329
|
+
params = {
|
330
|
+
'command' => 'resetPasswordForVirtualMachine',
|
331
|
+
'id' => server['id']
|
332
|
+
}
|
333
|
+
|
334
|
+
json = send_async_request(params)
|
335
|
+
json['virtualmachine']
|
336
|
+
end
|
337
|
+
|
338
|
+
##
|
339
|
+
# Start the server with the specified name.
|
340
|
+
#
|
341
|
+
|
342
|
+
def start_server(name)
|
343
|
+
server = get_server(name)
|
344
|
+
if !server || !server['id'] then
|
345
|
+
puts "\nError: Virtual machine '#{name}' does not exist"
|
346
|
+
exit 1
|
347
|
+
end
|
348
|
+
|
349
|
+
params = {
|
350
|
+
'command' => 'startVirtualMachine',
|
351
|
+
'id' => server['id']
|
352
|
+
}
|
353
|
+
|
354
|
+
json = send_async_request(params)
|
355
|
+
json['virtualmachine']
|
356
|
+
end
|
357
|
+
|
358
|
+
##
|
359
|
+
# Reboot the server with the specified name.
|
360
|
+
#
|
361
|
+
|
362
|
+
def reboot_server(name)
|
363
|
+
server = get_server(name)
|
364
|
+
if !server || !server['id'] then
|
365
|
+
puts "\nError: Virtual machine '#{name}' does not exist"
|
366
|
+
exit 1
|
367
|
+
end
|
368
|
+
|
369
|
+
params = {
|
370
|
+
'command' => 'rebootVirtualMachine',
|
371
|
+
'id' => server['id']
|
372
|
+
}
|
373
|
+
|
374
|
+
json = send_async_request(params)
|
375
|
+
json['virtualmachine']
|
376
|
+
end
|
377
|
+
|
378
|
+
##
|
379
|
+
# Finds the service offering with the specified name.
|
380
|
+
|
381
|
+
def get_service_offering(name)
|
382
|
+
|
383
|
+
# TODO: use name parameter
|
384
|
+
# listServiceOfferings in cosmic 2.2 doesn't seem to work
|
385
|
+
# when the name parameter is specified. When this is fixed,
|
386
|
+
# the name parameter should be added to the request.
|
387
|
+
params = {
|
388
|
+
'command' => 'listServiceOfferings'
|
389
|
+
}
|
390
|
+
json = send_request(params)
|
391
|
+
|
392
|
+
services = json['serviceoffering']
|
393
|
+
return nil unless services
|
394
|
+
|
395
|
+
services.each { |s|
|
396
|
+
if name.is_uuid? then
|
397
|
+
return s if s['id'] == name
|
398
|
+
else
|
399
|
+
return s if s['name'] == name
|
400
|
+
end
|
401
|
+
}
|
402
|
+
nil
|
403
|
+
end
|
404
|
+
|
405
|
+
##
|
406
|
+
# Lists all available service offerings.
|
407
|
+
|
408
|
+
def list_service_offerings
|
409
|
+
params = {
|
410
|
+
'command' => 'listServiceOfferings'
|
411
|
+
}
|
412
|
+
json = send_request(params)
|
413
|
+
json['serviceoffering'] || []
|
414
|
+
end
|
415
|
+
|
416
|
+
def list_security_groups
|
417
|
+
params = {
|
418
|
+
'command' => 'listSecurityGroups'
|
419
|
+
}
|
420
|
+
json = send_request(params)
|
421
|
+
json['securitygroups'] || []
|
422
|
+
end
|
423
|
+
|
424
|
+
##
|
425
|
+
# Finds the template with the specified name.
|
426
|
+
|
427
|
+
def get_template(name, zone_name=nil)
|
428
|
+
|
429
|
+
# TODO: use name parameter
|
430
|
+
# listTemplates in cosmic 2.2 doesn't seem to work
|
431
|
+
# when the name parameter is specified. When this is fixed,
|
432
|
+
# the name parameter should be added to the request.
|
433
|
+
|
434
|
+
zone = zone_name ? get_zone(zone_name) : get_default_zone
|
435
|
+
|
436
|
+
params = {
|
437
|
+
'command' => 'listTemplates',
|
438
|
+
'templateFilter' => 'executable',
|
439
|
+
}
|
440
|
+
params['zoneid'] = zone['id'] if zone
|
441
|
+
|
442
|
+
json = send_request(params)
|
443
|
+
|
444
|
+
templates = json['template']
|
445
|
+
return nil unless templates
|
446
|
+
|
447
|
+
templates.each { |t|
|
448
|
+
if name.is_uuid? then
|
449
|
+
return t if t['id'] == name
|
450
|
+
else
|
451
|
+
return t if t['name'] == name
|
452
|
+
end
|
453
|
+
}
|
454
|
+
nil
|
455
|
+
end
|
456
|
+
|
457
|
+
##
|
458
|
+
# Finds the iso with the specified name.
|
459
|
+
|
460
|
+
def get_iso(name, zone_name=nil)
|
461
|
+
zone = zone_name ? get_zone(zone_name) : get_default_zone
|
462
|
+
|
463
|
+
params = {
|
464
|
+
'command' => 'listIsos',
|
465
|
+
'isoFilter' => 'executable',
|
466
|
+
}
|
467
|
+
params['zoneid'] = zone['id'] if zone
|
468
|
+
|
469
|
+
json = send_request(params)
|
470
|
+
iso = json['iso']
|
471
|
+
return nil unless iso
|
472
|
+
|
473
|
+
iso.each { |i|
|
474
|
+
if name.is_uuid? then
|
475
|
+
return i if i['id'] == name
|
476
|
+
else
|
477
|
+
return i if i['name'] == name
|
478
|
+
end
|
479
|
+
}
|
480
|
+
nil
|
481
|
+
end
|
482
|
+
|
483
|
+
|
484
|
+
|
485
|
+
##
|
486
|
+
# Finds the disk offering with the specified name.
|
487
|
+
|
488
|
+
def get_disk_offering(name)
|
489
|
+
params = {
|
490
|
+
'command' => 'listDiskOfferings',
|
491
|
+
}
|
492
|
+
json = send_request(params)
|
493
|
+
disks = json['diskoffering']
|
494
|
+
|
495
|
+
return nil if !disks
|
496
|
+
|
497
|
+
disks.each { |d|
|
498
|
+
return d if d['name'] == name
|
499
|
+
}
|
500
|
+
nil
|
501
|
+
end
|
502
|
+
|
503
|
+
##
|
504
|
+
# Finds the volume with the specified name.
|
505
|
+
#
|
506
|
+
|
507
|
+
def get_volume(name)
|
508
|
+
params = {
|
509
|
+
'command' => 'listVolumes',
|
510
|
+
'name' => name
|
511
|
+
}
|
512
|
+
json = send_request(params)
|
513
|
+
volumes = json['volume']
|
514
|
+
|
515
|
+
if !volumes || volumes.empty? then
|
516
|
+
return nil
|
517
|
+
end
|
518
|
+
volume = volumes.select { |item| name == item['name'] }
|
519
|
+
volume.first
|
520
|
+
end
|
521
|
+
|
522
|
+
##
|
523
|
+
# Deletes the volume with the specified name.
|
524
|
+
#
|
525
|
+
|
526
|
+
def delete_volume(name)
|
527
|
+
volume = get_volume(name)
|
528
|
+
if !volume || !volume['id'] then
|
529
|
+
puts "\nError: Volume '#{name}' does not exist"
|
530
|
+
exit 1
|
531
|
+
end
|
532
|
+
|
533
|
+
params = {
|
534
|
+
'command' => 'deleteVolume',
|
535
|
+
'id' => volume['id']
|
536
|
+
}
|
537
|
+
|
538
|
+
json = send_request(params)
|
539
|
+
json['success']
|
540
|
+
end
|
541
|
+
|
542
|
+
##
|
543
|
+
# Lists all templates that match the specified filter.
|
544
|
+
#
|
545
|
+
# Allowable filter values are:
|
546
|
+
#
|
547
|
+
# * featured - templates that are featured and are public
|
548
|
+
# * self - templates that have been registered/created by the owner
|
549
|
+
# * self-executable - templates that have been registered/created by the owner that can be used to deploy a new VM
|
550
|
+
# * executable - all templates that can be used to deploy a new VM
|
551
|
+
# * community - templates that are public
|
552
|
+
|
553
|
+
def list_templates(filter)
|
554
|
+
filter ||= 'featured'
|
555
|
+
params = {
|
556
|
+
'command' => 'listTemplates',
|
557
|
+
'templateFilter' => filter
|
558
|
+
}
|
559
|
+
json = send_request(params)
|
560
|
+
json['template'] || []
|
561
|
+
end
|
562
|
+
|
563
|
+
#Fetch project with the specified name
|
564
|
+
def get_project(name)
|
565
|
+
params = {
|
566
|
+
'command' => 'listProjects',
|
567
|
+
'listall' => true
|
568
|
+
}
|
569
|
+
|
570
|
+
json = send_request(params)
|
571
|
+
projects = json['project']
|
572
|
+
return nil unless projects
|
573
|
+
|
574
|
+
projects.each { |n|
|
575
|
+
if name.is_uuid? then
|
576
|
+
return n if n['id'] == name
|
577
|
+
else
|
578
|
+
return n if n['name'] == name
|
579
|
+
end
|
580
|
+
}
|
581
|
+
nil
|
582
|
+
end
|
583
|
+
|
584
|
+
# Fetch acls applied to vpc
|
585
|
+
def get_networkAcls(networkid)
|
586
|
+
params = {
|
587
|
+
'command' => 'listNetworkACLLists',
|
588
|
+
'networkid' => networkid
|
589
|
+
}
|
590
|
+
|
591
|
+
json = send_request(params)
|
592
|
+
acls = json['networkacllist']
|
593
|
+
|
594
|
+
return nil unless acls
|
595
|
+
acls
|
596
|
+
end
|
597
|
+
|
598
|
+
##
|
599
|
+
# Filter data on regex or just on string
|
600
|
+
|
601
|
+
def data_filter(data, filters)
|
602
|
+
filters.split(',').each do |filter|
|
603
|
+
field = filter.split(':').first.strip.downcase
|
604
|
+
search = filter.split(':').last.strip
|
605
|
+
if search =~ /^\/.*\/?/
|
606
|
+
data = data.find_all { |k| k["#{field}"].to_s =~ search.to_regexp } if field && search
|
607
|
+
else
|
608
|
+
data = data.find_all { |k| k["#{field}"].to_s == "#{search}" } if field && search
|
609
|
+
end
|
610
|
+
end
|
611
|
+
data
|
612
|
+
end
|
613
|
+
|
614
|
+
|
615
|
+
##
|
616
|
+
# Finds the network with the specified name.
|
617
|
+
|
618
|
+
def get_network(name)
|
619
|
+
params = {
|
620
|
+
'command' => 'listNetworks'
|
621
|
+
}
|
622
|
+
json = send_request(params)
|
623
|
+
|
624
|
+
networks = json['network']
|
625
|
+
return nil unless networks
|
626
|
+
|
627
|
+
networks.each { |n|
|
628
|
+
if name.is_uuid? then
|
629
|
+
return n if n['id'] == name
|
630
|
+
else
|
631
|
+
return n if n['name'] == name
|
632
|
+
end
|
633
|
+
}
|
634
|
+
nil
|
635
|
+
end
|
636
|
+
|
637
|
+
##
|
638
|
+
# Finds the default network.
|
639
|
+
|
640
|
+
def get_default_network(zone)
|
641
|
+
params = {
|
642
|
+
'command' => 'listNetworks',
|
643
|
+
'isDefault' => true,
|
644
|
+
'zoneid' => zone
|
645
|
+
}
|
646
|
+
json = send_request(params)
|
647
|
+
|
648
|
+
networks = json['network']
|
649
|
+
return nil if !networks || networks.empty?
|
650
|
+
|
651
|
+
default = networks.first
|
652
|
+
return default if networks.length == 1
|
653
|
+
|
654
|
+
networks.each { |n|
|
655
|
+
if n['type'] == 'Direct' then
|
656
|
+
default = n
|
657
|
+
break
|
658
|
+
end
|
659
|
+
}
|
660
|
+
|
661
|
+
default
|
662
|
+
end
|
663
|
+
|
664
|
+
##
|
665
|
+
# Lists all available networks.
|
666
|
+
|
667
|
+
def list_networks
|
668
|
+
params = {
|
669
|
+
'command' => 'listNetworks'
|
670
|
+
}
|
671
|
+
json = send_request(params)
|
672
|
+
json['network'] || []
|
673
|
+
end
|
674
|
+
|
675
|
+
##
|
676
|
+
# Finds the zone with the specified name.
|
677
|
+
|
678
|
+
def get_zone(name)
|
679
|
+
params = {
|
680
|
+
'command' => 'listZones',
|
681
|
+
'available' => 'true'
|
682
|
+
}
|
683
|
+
json = send_request(params)
|
684
|
+
|
685
|
+
networks = json['zone']
|
686
|
+
return nil unless networks
|
687
|
+
|
688
|
+
networks.each { |z|
|
689
|
+
if name.is_uuid? then
|
690
|
+
return z if z['id'] == name
|
691
|
+
else
|
692
|
+
return z if z['name'] == name
|
693
|
+
end
|
694
|
+
}
|
695
|
+
nil
|
696
|
+
end
|
697
|
+
|
698
|
+
def add_nic_to_vm(network_id, server_id, ipaddr=nil)
|
699
|
+
params = {
|
700
|
+
'command' => 'addNicToVirtualMachine',
|
701
|
+
'networkid' => network_id,
|
702
|
+
'virtualmachineid' => server_id,
|
703
|
+
}
|
704
|
+
|
705
|
+
unless ipaddr.nil?
|
706
|
+
params['ipaddress'] = ipaddr
|
707
|
+
end
|
708
|
+
|
709
|
+
json = send_async_request(params)
|
710
|
+
json['virtualmachine']
|
711
|
+
end
|
712
|
+
|
713
|
+
def remove_nic_from_vm(nic_id, server_id)
|
714
|
+
params = {
|
715
|
+
'command' => 'removeNicFromVirtualMachine',
|
716
|
+
'nicid' => nic_id,
|
717
|
+
'virtualmachineid' => server_id,
|
718
|
+
}
|
719
|
+
|
720
|
+
json = send_async_request(params)
|
721
|
+
json['virtualmachine']
|
722
|
+
end
|
723
|
+
|
724
|
+
##
|
725
|
+
# Finds the default zone for your account.
|
726
|
+
|
727
|
+
def get_default_zone
|
728
|
+
params = {
|
729
|
+
'command' => 'listZones',
|
730
|
+
'available' => 'true'
|
731
|
+
}
|
732
|
+
json = send_request(params)
|
733
|
+
|
734
|
+
zones = json['zone']
|
735
|
+
return nil unless zones
|
736
|
+
# zones.sort! # sort zones so we always return the same zone
|
737
|
+
# !this gives error in our production environment so need to retest this
|
738
|
+
zones.first
|
739
|
+
end
|
740
|
+
|
741
|
+
##
|
742
|
+
# Lists all available zones.
|
743
|
+
|
744
|
+
def list_zones
|
745
|
+
params = {
|
746
|
+
'command' => 'listZones',
|
747
|
+
'available' => 'true'
|
748
|
+
}
|
749
|
+
json = send_request(params)
|
750
|
+
json['zone'] || []
|
751
|
+
end
|
752
|
+
|
753
|
+
##
|
754
|
+
# Finds networkid associated with public router IP
|
755
|
+
def get_networkid_from_ip_address(ip_address)
|
756
|
+
params = {
|
757
|
+
'command' => 'listPublicIpAddresses',
|
758
|
+
'ipaddress' => ip_address
|
759
|
+
}
|
760
|
+
json = send_request(params)
|
761
|
+
return nil unless json['associatednetworkid']
|
762
|
+
json['associatednetworkid'].first
|
763
|
+
end
|
764
|
+
|
765
|
+
##
|
766
|
+
# Finds the public ip address for a given ip address string.
|
767
|
+
|
768
|
+
def get_public_ip_address(ip_address)
|
769
|
+
params = {
|
770
|
+
'command' => 'listPublicIpAddresses',
|
771
|
+
'ipaddress' => ip_address
|
772
|
+
}
|
773
|
+
json = send_request(params)
|
774
|
+
return nil unless json['publicipaddress']
|
775
|
+
json['publicipaddress'].first
|
776
|
+
end
|
777
|
+
|
778
|
+
def list_public_ip_addresses(listall=false)
|
779
|
+
params = { 'command' => 'listPublicIpAddresses' }
|
780
|
+
params['listall'] = listall
|
781
|
+
|
782
|
+
json = send_request(params)
|
783
|
+
return json['publicipaddress'] || []
|
784
|
+
end
|
785
|
+
##
|
786
|
+
# Acquires and associates a public IP to an account.
|
787
|
+
|
788
|
+
def associate_ip_address(zone_id, networks)
|
789
|
+
params = {
|
790
|
+
'command' => 'associateIpAddress',
|
791
|
+
'zoneId' => zone_id
|
792
|
+
}
|
793
|
+
#Choose the first network from the list
|
794
|
+
if networks.nil? || networks.empty?
|
795
|
+
default_network = get_default_network(zone_id)
|
796
|
+
params['networkId'] = default_network['id']
|
797
|
+
else
|
798
|
+
params['networkId'] = get_network(networks.first)['id']
|
799
|
+
end
|
800
|
+
Chef::Log.debug("associate ip params: #{params}")
|
801
|
+
json = send_async_request(params)
|
802
|
+
json['ipaddress']
|
803
|
+
end
|
804
|
+
|
805
|
+
def enable_static_nat(ipaddress_id, virtualmachine_id)
|
806
|
+
params = {
|
807
|
+
'command' => 'enableStaticNat',
|
808
|
+
'ipAddressId' => ipaddress_id,
|
809
|
+
'virtualmachineId' => virtualmachine_id
|
810
|
+
}
|
811
|
+
send_request(params)
|
812
|
+
end
|
813
|
+
|
814
|
+
def disable_static_nat(ipaddress)
|
815
|
+
params = {
|
816
|
+
'command' => 'disableStaticNat',
|
817
|
+
'ipAddressId' => ipaddress['id']
|
818
|
+
}
|
819
|
+
send_async_request(params)
|
820
|
+
end
|
821
|
+
|
822
|
+
def create_ip_fwd_rule(ipaddress_id, protocol, start_port, end_port)
|
823
|
+
params = {
|
824
|
+
'command' => 'createIpForwardingRule',
|
825
|
+
'ipaddressId' => ipaddress_id,
|
826
|
+
'protocol' => protocol,
|
827
|
+
'startport' => start_port,
|
828
|
+
'endport' => end_port
|
829
|
+
}
|
830
|
+
|
831
|
+
send_async_request(params)
|
832
|
+
end
|
833
|
+
|
834
|
+
def create_firewall_rule(ipaddress_id, protocol, param3, param4, cidr_list)
|
835
|
+
if protocol == "ICMP"
|
836
|
+
params = {
|
837
|
+
'command' => 'createFirewallRule',
|
838
|
+
'ipaddressId' => ipaddress_id,
|
839
|
+
'protocol' => protocol,
|
840
|
+
'icmptype' => param3,
|
841
|
+
'icmpcode' => param4,
|
842
|
+
'cidrlist' => cidr_list
|
843
|
+
}
|
844
|
+
else
|
845
|
+
params = {
|
846
|
+
'command' => 'createFirewallRule',
|
847
|
+
'ipaddressId' => ipaddress_id,
|
848
|
+
'protocol' => protocol,
|
849
|
+
'startport' => param3,
|
850
|
+
'endport' => param4,
|
851
|
+
'cidrlist' => cidr_list
|
852
|
+
}
|
853
|
+
end
|
854
|
+
send_async_request(params)
|
855
|
+
end
|
856
|
+
|
857
|
+
##
|
858
|
+
# Disassociates an ip address from the account.
|
859
|
+
#
|
860
|
+
# Returns true if successful, false otherwise.
|
861
|
+
|
862
|
+
def disassociate_ip_address(id)
|
863
|
+
params = {
|
864
|
+
'command' => 'disassociateIpAddress',
|
865
|
+
'id' => id
|
866
|
+
}
|
867
|
+
json = send_async_request(params)
|
868
|
+
json['success']
|
869
|
+
end
|
870
|
+
|
871
|
+
##
|
872
|
+
# Lists all port forwarding rules.
|
873
|
+
|
874
|
+
def list_port_forwarding_rules(ip_address_id=nil, listall=false)
|
875
|
+
params = { 'command' => 'listPortForwardingRules' }
|
876
|
+
params['ipAddressId'] = ip_address_id if ip_address_id
|
877
|
+
params['listall'] = listall
|
878
|
+
json = send_request(params)
|
879
|
+
json['portforwardingrule']
|
880
|
+
end
|
881
|
+
|
882
|
+
##
|
883
|
+
# Gets the SSH port forwarding rule for the specified server.
|
884
|
+
|
885
|
+
def get_ssh_port_forwarding_rule(server, cached_rules=nil)
|
886
|
+
rules = cached_rules || list_port_forwarding_rules || []
|
887
|
+
rules.find_all { |r|
|
888
|
+
r['virtualmachineid'] == server['id'] &&
|
889
|
+
r['privateport'] == '22'&&
|
890
|
+
r['publicport'] == '22'
|
891
|
+
}.first
|
892
|
+
end
|
893
|
+
|
894
|
+
##
|
895
|
+
# Gets the WINRM port forwarding rule for the specified server.
|
896
|
+
|
897
|
+
def get_winrm_port_forwarding_rule(server, cached_rules=nil)
|
898
|
+
rules = cached_rules || list_port_forwarding_rules || []
|
899
|
+
rules.find_all { |r|
|
900
|
+
r['virtualmachineid'] == server['id'] &&
|
901
|
+
(r['privateport'] == '5985' &&
|
902
|
+
r['publicport'] == '5985') ||
|
903
|
+
(r['privateport'] == '5986' &&
|
904
|
+
r['publicport'] == '5986')
|
905
|
+
}.first
|
906
|
+
end
|
907
|
+
|
908
|
+
##
|
909
|
+
# Creates a port forwarding rule.
|
910
|
+
|
911
|
+
def create_port_forwarding_rule(ip_address_id, private_port, protocol, public_port, virtual_machine_id)
|
912
|
+
params = {
|
913
|
+
'command' => 'createPortForwardingRule',
|
914
|
+
'ipAddressId' => ip_address_id,
|
915
|
+
'privatePort' => private_port,
|
916
|
+
'protocol' => protocol,
|
917
|
+
'publicPort' => public_port,
|
918
|
+
'virtualMachineId' => virtual_machine_id
|
919
|
+
}
|
920
|
+
json = send_async_request(params)
|
921
|
+
json['portforwardingrule']
|
922
|
+
end
|
923
|
+
|
924
|
+
def http_client_builder
|
925
|
+
http_proxy = proxy_uri
|
926
|
+
if http_proxy.nil?
|
927
|
+
Net::HTTP
|
928
|
+
else
|
929
|
+
Chef::Log.debug("Using #{http_proxy.host}:#{http_proxy.port} for proxy")
|
930
|
+
user = http_proxy.user if http_proxy.user
|
931
|
+
pass = http_proxy.password if http_proxy.password
|
932
|
+
Net::HTTP.Proxy(http_proxy.host, http_proxy.port, user, pass)
|
933
|
+
end
|
934
|
+
end
|
935
|
+
|
936
|
+
def proxy_uri
|
937
|
+
return nil if @api_proxy.nil?
|
938
|
+
result = URI.parse(@api_proxy)
|
939
|
+
return result unless result.host.nil? || result.host.empty?
|
940
|
+
nil
|
941
|
+
end
|
942
|
+
|
943
|
+
##
|
944
|
+
# Sends a synchronous request to the cosmic API and returns the response as a Hash.
|
945
|
+
#
|
946
|
+
# The wrapper element of the response (e.g. mycommandresponse) is discarded and the
|
947
|
+
# contents of that element are returned.
|
948
|
+
|
949
|
+
def send_request(params)
|
950
|
+
if @project_id
|
951
|
+
params['projectId'] = @project_id
|
952
|
+
end
|
953
|
+
params['response'] = 'json'
|
954
|
+
params['apiKey'] = @api_key
|
955
|
+
|
956
|
+
params_arr = []
|
957
|
+
params.sort.each { |elem|
|
958
|
+
params_arr << elem[0].to_s + '=' + CGI.escape(elem[1].to_s).gsub('+', '%20').gsub(' ','%20')
|
959
|
+
}
|
960
|
+
data = params_arr.join('&')
|
961
|
+
signature = OpenSSL::HMAC.digest('sha1', @secret_key, data.downcase)
|
962
|
+
signature = Base64.encode64(signature).chomp
|
963
|
+
signature = CGI.escape(signature)
|
964
|
+
|
965
|
+
if @api_url.nil? || @api_url.empty?
|
966
|
+
puts "Error: Please specify a valid API URL."
|
967
|
+
exit 1
|
968
|
+
end
|
969
|
+
|
970
|
+
url = "#{@api_url}?#{data}&signature=#{signature}"
|
971
|
+
Chef::Log.debug("URL: #{url}")
|
972
|
+
uri = URI.parse(url)
|
973
|
+
|
974
|
+
http = http_client_builder.new(uri.host, uri.port)
|
975
|
+
|
976
|
+
if uri.scheme == "https"
|
977
|
+
http.use_ssl = true
|
978
|
+
# Still need to do some testing on SSL, so will fix this later
|
979
|
+
if @no_ssl_verify
|
980
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
981
|
+
else
|
982
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
983
|
+
end
|
984
|
+
end
|
985
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
986
|
+
response = http.request(request)
|
987
|
+
|
988
|
+
if !response.is_a?(Net::HTTPOK) then
|
989
|
+
case response.code
|
990
|
+
when "432"
|
991
|
+
puts "\n"
|
992
|
+
puts "Error #{response.code}: Your account does not have the right to execute this command is locked or the command does not exist."
|
993
|
+
else
|
994
|
+
puts "Error #{response.code}: #{response.message}"
|
995
|
+
puts JSON.pretty_generate(JSON.parse(response.body))
|
996
|
+
puts "URL: #{url}"
|
997
|
+
end
|
998
|
+
exit 1
|
999
|
+
end
|
1000
|
+
|
1001
|
+
json = JSON.parse(response.body)
|
1002
|
+
json[params['command'].downcase + 'response']
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
##
|
1006
|
+
# Sends an asynchronous request and waits for the response.
|
1007
|
+
#
|
1008
|
+
# The contents of the 'jobresult' element are returned upon completion of the command.
|
1009
|
+
|
1010
|
+
def send_async_request(params)
|
1011
|
+
|
1012
|
+
json = send_request(params)
|
1013
|
+
|
1014
|
+
params = {
|
1015
|
+
'command' => 'queryAsyncJobResult',
|
1016
|
+
'jobId' => json['jobid']
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
max_tries = (ASYNC_TIMEOUT / ASYNC_POLL_INTERVAL).round
|
1020
|
+
max_tries.times do
|
1021
|
+
json = send_request(params)
|
1022
|
+
status = json['jobstatus']
|
1023
|
+
|
1024
|
+
print "."
|
1025
|
+
|
1026
|
+
if status == 1 then
|
1027
|
+
print "\n"
|
1028
|
+
return json['jobresult']
|
1029
|
+
elsif status == 2 then
|
1030
|
+
print "\n"
|
1031
|
+
puts "Request failed (#{json['jobresultcode']}): #{json['jobresult']}"
|
1032
|
+
exit 1
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
STDOUT.flush
|
1036
|
+
sleep ASYNC_POLL_INTERVAL
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
print "\n"
|
1040
|
+
puts "Error: Asynchronous request timed out"
|
1041
|
+
exit 1
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
end # class
|
1045
|
+
end
|
1046
|
+
|