azure 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.txt +7 -1
- data/README.md +32 -0
- data/lib/azure/version.rb +1 -1
- data/lib/azure/virtual_machine_image_management/serialization.rb +3 -0
- data/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb +1 -1
- data/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb +5 -0
- data/lib/azure/virtual_machine_management/serialization.rb +179 -63
- data/lib/azure/virtual_machine_management/virtual_machine.rb +1 -0
- data/lib/azure/virtual_machine_management/virtual_machine_management_service.rb +206 -40
- data/test/fixtures/list_images.xml +1 -1
- data/test/fixtures/virtual_machine.xml +5 -0
- data/test/integration/vm/VM_Create_test.rb +46 -44
- data/test/integration/vm/VM_Delete_test.rb +19 -19
- data/test/integration/vm/VM_Operations_test.rb +174 -0
- data/test/support/virtual_machine_name_generator.rb +19 -34
- data/test/unit/virtual_machine_image_management/virtual_machine_image_management_service_test.rb +8 -3
- data/test/unit/virtual_machine_management/serialization_test.rb +127 -40
- data/test/unit/virtual_machine_management/virtual_machine_management_service_test.rb +205 -153
- metadata +3 -4
- data/test/integration/vm/VM_List_test.rb +0 -71
- data/test/integration/vm/VM_ShutDown_test.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2218a9a37d85a587e477daa5f91d8c06d0eb3f59
|
4
|
+
data.tar.gz: 39efa8b58227090e87058d9aa4765251d423f4ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a47b93b9e6e87ea04bc0458569e16d3b75046f8a3ce178c4d0914078f94d4b78d4ffd3ac124c8870033f09cf2d6932fec147ed739d43f1fb71e8cb72620a32ca
|
7
|
+
data.tar.gz: f15f40f69a33d35902b0f585c1de2a405a7468b7c21befe2b0902d0d61da233923c8bb417b4d0212e8ddab87937a9607c32d13220e2316e9d492df2d2a314c40
|
data/ChangeLog.txt
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
2014.03.15 - version 0.6.2
|
2
|
+
* Restart Virtual Machine
|
3
|
+
* Add disk to Virtual Machine
|
4
|
+
* Add/Update Virtual Machine endpoints
|
5
|
+
* Delete Virtual Machine endpoint
|
6
|
+
|
1
7
|
2014.02.18 - version 0.6.1
|
2
|
-
* Fixed http redirection error
|
8
|
+
* Fixed http redirection error
|
3
9
|
* Add a new role to existing deployment
|
4
10
|
* Add support for including VMs in availability sets
|
5
11
|
|
data/README.md
CHANGED
@@ -369,6 +369,38 @@ virtual_machine_service.shutdown_virtual_machine('vm_name', 'cloud_service_name'
|
|
369
369
|
#API to start Virtual Machine
|
370
370
|
virtual_machine_service.start_virtual_machine('vm_name', 'cloud_service_name')
|
371
371
|
|
372
|
+
#API to restart Virtual Machine
|
373
|
+
virtual_machine_service.restart_virtual_machine('vm_name', 'cloud_service_name')
|
374
|
+
|
375
|
+
#API for add disk to Virtual Machine
|
376
|
+
lun = 1 #Valid LUN values are 0 through 15.
|
377
|
+
options = {
|
378
|
+
:disk_label => 'disk-label',
|
379
|
+
:disk_size => 100, #In GB
|
380
|
+
:import => false
|
381
|
+
}
|
382
|
+
virtual_machine_service.add_data_disk('vm_name', 'cloud_service_name', lun, options)
|
383
|
+
|
384
|
+
#API to add/update Virtual Machine endpoints
|
385
|
+
endpoint1 = {
|
386
|
+
:name => 'ep-1',
|
387
|
+
:public_port => 996,
|
388
|
+
:local_port => 998,
|
389
|
+
:protocol => 'TCP',
|
390
|
+
}
|
391
|
+
endpoint2 = {
|
392
|
+
:name => 'ep-2',
|
393
|
+
:public_port => 997,
|
394
|
+
:local_port => 997,
|
395
|
+
:protocol => 'TCP',
|
396
|
+
:load_balancer_name => ‘lb-ep2’,
|
397
|
+
:load_balancer => {:protocol => 'http', :path => 'hello'}
|
398
|
+
}
|
399
|
+
virtual_machine_service.update_endpoints('vm_name', 'cloud_service_name', endpoint1, endpoint2)
|
400
|
+
|
401
|
+
#API to delete Virtual Machine endpoint
|
402
|
+
virtual_machine_service.delete_endpoint('vm_name', 'cloud_service_name', 'endpoint_name')
|
403
|
+
|
372
404
|
#API to delete Virtual Machine
|
373
405
|
virtual_machine_service.delete_virtual_machine('vm_name', 'cloud_service_name')
|
374
406
|
|
data/lib/azure/version.rb
CHANGED
@@ -39,7 +39,10 @@ module Azure
|
|
39
39
|
disks.each do |disk_node|
|
40
40
|
disk = VirtualMachineDisk.new
|
41
41
|
disk.name = xml_content(disk_node, 'Name')
|
42
|
+
disk.os_type = xml_content(disk_node, 'OS')
|
42
43
|
disk.attached = !xml_content(disk_node,'AttachedTo').empty?
|
44
|
+
disk.image = xml_content(disk_node, 'SourceImageName')
|
45
|
+
disk.size = xml_content(disk_node, 'LogicalDiskSizeInGB')
|
43
46
|
os_disks << disk
|
44
47
|
end
|
45
48
|
os_disks
|
@@ -50,6 +50,11 @@ module Azure
|
|
50
50
|
Serialization.disks_from_xml(response)
|
51
51
|
end
|
52
52
|
|
53
|
+
def get_virtual_machine_disk(disk_name)
|
54
|
+
disk = list_virtual_machine_disks.select {|x| x.name == disk_name}
|
55
|
+
disk.first
|
56
|
+
end
|
57
|
+
|
53
58
|
# Public: Deletes the specified data or operating system disk from the image repository.
|
54
59
|
#
|
55
60
|
# Returns None
|
@@ -43,6 +43,18 @@ module Azure
|
|
43
43
|
builder.doc.to_xml
|
44
44
|
end
|
45
45
|
|
46
|
+
def self.restart_virtual_machine_to_xml
|
47
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
48
|
+
xml.RestartRoleOperation(
|
49
|
+
'xmlns' => 'http://schemas.microsoft.com/windowsazure',
|
50
|
+
'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance'
|
51
|
+
) do
|
52
|
+
xml.OperationType 'RestartRoleOperation'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
builder.doc.to_xml
|
56
|
+
end
|
57
|
+
|
46
58
|
def self.deployment_to_xml(params, options)
|
47
59
|
options[:deployment_name] ||= options[:cloud_service_name]
|
48
60
|
builder = Nokogiri::XML::Builder.new do |xml|
|
@@ -101,6 +113,7 @@ module Azure
|
|
101
113
|
end
|
102
114
|
|
103
115
|
def self.provisioning_configuration_to_xml(xml, params, options)
|
116
|
+
fingerprint = params[:certificate][:fingerprint]
|
104
117
|
if options[:os_type] == 'Linux'
|
105
118
|
xml.ConfigurationSet('i:type' => 'LinuxProvisioningConfigurationSet') do
|
106
119
|
xml.ConfigurationSetType 'LinuxProvisioningConfiguration'
|
@@ -110,11 +123,11 @@ module Azure
|
|
110
123
|
xml.UserPassword params[:password]
|
111
124
|
xml.DisableSshPasswordAuthentication 'false'
|
112
125
|
end
|
113
|
-
if
|
126
|
+
if fingerprint
|
114
127
|
xml.SSH do
|
115
128
|
xml.PublicKeys do
|
116
129
|
xml.PublicKey do
|
117
|
-
xml.Fingerprint
|
130
|
+
xml.Fingerprint fingerprint
|
118
131
|
xml.Path "/home/#{params[:vm_user]}/.ssh/authorized_keys"
|
119
132
|
end
|
120
133
|
end
|
@@ -139,7 +152,7 @@ module Azure
|
|
139
152
|
if options[:winrm_transport].include?('https')
|
140
153
|
xml.Listener do
|
141
154
|
xml.Protocol 'Https'
|
142
|
-
xml.CertificateThumbprint
|
155
|
+
xml.CertificateThumbprint fingerprint if fingerprint
|
143
156
|
end
|
144
157
|
end
|
145
158
|
end
|
@@ -152,73 +165,82 @@ module Azure
|
|
152
165
|
|
153
166
|
def self.default_endpoints_to_xml(xml, options)
|
154
167
|
os_type = options[:os_type]
|
168
|
+
endpoints = []
|
155
169
|
if os_type == 'Linux'
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
170
|
+
endpoints << {
|
171
|
+
name: 'SSH',
|
172
|
+
public_port: options[:ssh_port] || '22',
|
173
|
+
protocol: 'TCP',
|
174
|
+
local_port: '22'
|
175
|
+
}
|
176
|
+
elsif os_type == 'Windows' && options[:winrm_transport]
|
177
|
+
if options[:winrm_transport].include?('http')
|
178
|
+
endpoints << {
|
179
|
+
name: 'WinRm-Http',
|
180
|
+
public_port: '5985',
|
181
|
+
protocol: 'TCP',
|
182
|
+
local_port: '5985'
|
183
|
+
}
|
161
184
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
170
|
-
end
|
171
|
-
if options[:winrm_transport] && options[:winrm_transport].include?('https')
|
172
|
-
xml.InputEndpoint do
|
173
|
-
xml.LocalPort '5986'
|
174
|
-
xml.Name 'WinRm-Https'
|
175
|
-
xml.Port '5986'
|
176
|
-
xml.Protocol 'TCP'
|
177
|
-
end
|
185
|
+
if options[:winrm_transport].include?('https')
|
186
|
+
endpoints << {
|
187
|
+
name: 'PowerShell',
|
188
|
+
public_port: '5986',
|
189
|
+
protocol: 'TCP',
|
190
|
+
local_port: '5986'
|
191
|
+
}
|
178
192
|
end
|
179
193
|
end
|
194
|
+
endpoints_to_xml(xml, endpoints)
|
180
195
|
end
|
181
196
|
|
182
197
|
def self.tcp_endpoints_to_xml(xml, tcp_endpoints)
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
xml.Port ports[0]
|
194
|
-
end
|
195
|
-
xml.Protocol 'TCP'
|
196
|
-
end
|
198
|
+
endpoints = []
|
199
|
+
tcp_endpoints.split(',').each do |endpoint|
|
200
|
+
ports = endpoint.split(':')
|
201
|
+
tcp_ep = {}
|
202
|
+
if ports.length > 1
|
203
|
+
tcp_ep[:name] = 'TCP-PORT-' + ports[1]
|
204
|
+
tcp_ep[:public_port] = ports[1]
|
205
|
+
else
|
206
|
+
tcp_ep[:name] = 'TCP-PORT-' + ports[0]
|
207
|
+
tcp_ep[:public_port] = ports[0]
|
197
208
|
end
|
209
|
+
tcp_ep[:local_port] = ports[0]
|
210
|
+
tcp_ep[:protocol] = 'TCP'
|
211
|
+
endpoints << tcp_ep
|
198
212
|
end
|
213
|
+
endpoints_to_xml(xml, endpoints)
|
199
214
|
end
|
200
215
|
|
201
216
|
def self.virtual_machines_from_xml(deployXML, cloud_service_name)
|
202
|
-
unless
|
203
|
-
|
217
|
+
unless deployXML.nil? or deployXML.at_css('Deployment Name').nil?
|
218
|
+
instances = deployXML.css('Deployment RoleInstanceList RoleInstance')
|
219
|
+
roles = deployXML.css('Deployment RoleList Role')
|
220
|
+
ip = deployXML.css('Deployment VirtualIPs VirtualIP')
|
204
221
|
vms = []
|
205
|
-
|
222
|
+
instances.each do |instance|
|
206
223
|
vm = VirtualMachine.new
|
224
|
+
role_name = xml_content(instance, 'RoleName')
|
207
225
|
vm.status = xml_content(instance, 'InstanceStatus')
|
208
|
-
vm.vm_name =
|
226
|
+
vm.vm_name = role_name.downcase
|
227
|
+
vm.ipaddress = xml_content(ip, 'Address')
|
209
228
|
vm.role_size = xml_content(instance, 'InstanceSize')
|
210
229
|
vm.hostname = xml_content(instance, 'HostName')
|
211
230
|
vm.cloud_service_name = cloud_service_name.downcase
|
212
231
|
vm.deployment_name = xml_content(deployXML, 'Deployment Name')
|
213
232
|
vm.deployment_status = xml_content(deployXML, 'Deployment Status')
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
233
|
+
vm.virtual_network_name = xml_content(
|
234
|
+
deployXML.css('Deployment'),
|
235
|
+
'VirtualNetworkName'
|
236
|
+
)
|
237
|
+
roles.each do |role|
|
238
|
+
if xml_content(role, 'RoleName') == role_name
|
219
239
|
vm.availability_set_name = xml_content(role, 'AvailabilitySetName')
|
240
|
+
endpoints_from_xml(role, vm)
|
220
241
|
vm.os_type = xml_content(role, 'OSVirtualHardDisk OS')
|
221
242
|
vm.disk_name = xml_content(role, 'OSVirtualHardDisk DiskName')
|
243
|
+
vm.media_link = xml_content(role, 'OSVirtualHardDisk MediaLink')
|
222
244
|
break
|
223
245
|
end
|
224
246
|
end
|
@@ -228,30 +250,124 @@ module Azure
|
|
228
250
|
end
|
229
251
|
end
|
230
252
|
|
231
|
-
def self.
|
253
|
+
def self.endpoints_from_xml(rolesXML, vm)
|
232
254
|
vm.tcp_endpoints = []
|
233
255
|
vm.udp_endpoints = []
|
234
|
-
endpoints = rolesXML.css('
|
256
|
+
endpoints = rolesXML.css('ConfigurationSets ConfigurationSet InputEndpoints InputEndpoint')
|
235
257
|
endpoints.each do |endpoint|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
258
|
+
lb_name = xml_content(endpoint, 'LoadBalancedEndpointSetName')
|
259
|
+
ep = {}
|
260
|
+
ep[:name] = xml_content(endpoint, 'Name')
|
261
|
+
ep[:vip] = xml_content(endpoint, 'Vip')
|
262
|
+
ep[:public_port] = xml_content(endpoint, 'Port')
|
263
|
+
ep[:local_port] = xml_content(endpoint, 'LocalPort')
|
264
|
+
ep[:protocol] = xml_content(endpoint, 'Protocol')
|
265
|
+
server_return = xml_content(endpoint, 'EnableDirectServerReturn')
|
266
|
+
ep[:direct_server_return] = server_return if !server_return.empty?
|
267
|
+
unless lb_name.empty?
|
268
|
+
ep[:protocol] = endpoint.css('Protocol').last.text
|
269
|
+
ep[:load_balancer_name] = lb_name
|
270
|
+
lb_port = xml_content(endpoint, 'LoadBalancerProbe Port')
|
271
|
+
lb_protocol = xml_content(endpoint, 'LoadBalancerProbe Protocol')
|
272
|
+
lb_path = xml_content(endpoint, 'LoadBalancerProbe Path')
|
273
|
+
lb_interval = xml_content(
|
274
|
+
endpoint,
|
275
|
+
'LoadBalancerProbe IntervalInSeconds'
|
276
|
+
)
|
277
|
+
lb_timeout = xml_content(
|
278
|
+
endpoint,
|
279
|
+
'LoadBalancerProbe TimeoutInSeconds'
|
280
|
+
)
|
281
|
+
ep[:load_balancer] = {
|
282
|
+
port: lb_port,
|
283
|
+
path: lb_path,
|
284
|
+
protocol: lb_protocol,
|
285
|
+
interval: lb_interval,
|
286
|
+
timeout: lb_timeout
|
287
|
+
}
|
242
288
|
end
|
243
|
-
|
244
|
-
|
245
|
-
hash['Vip'] = xml_content(endpoint, 'Vip')
|
246
|
-
hash['PublicPort'] = xml_content(endpoint, 'PublicPort')
|
247
|
-
hash['LocalPort'] = xml_content(endpoint, 'LocalPort')
|
248
|
-
if xml_content(endpoint, 'Protocol') == 'tcp'
|
249
|
-
vm.tcp_endpoints << hash
|
289
|
+
if ep[:protocol].downcase == 'tcp'
|
290
|
+
vm.tcp_endpoints << ep
|
250
291
|
else
|
251
|
-
vm.udp_endpoints <<
|
292
|
+
vm.udp_endpoints << ep
|
252
293
|
end
|
253
294
|
end
|
254
295
|
end
|
296
|
+
|
297
|
+
def self.update_role_to_xml(endpoints, vm)
|
298
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
299
|
+
xml.PersistentVMRole(
|
300
|
+
'xmlns' => 'http://schemas.microsoft.com/windowsazure',
|
301
|
+
'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance'
|
302
|
+
) do
|
303
|
+
xml.ConfigurationSets do
|
304
|
+
xml.ConfigurationSet do
|
305
|
+
xml.ConfigurationSetType 'NetworkConfiguration'
|
306
|
+
xml.InputEndpoints do
|
307
|
+
endpoints_to_xml(xml, endpoints)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
xml.OSVirtualHardDisk do
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
builder.doc.to_xml
|
316
|
+
end
|
317
|
+
|
318
|
+
def self.endpoints_to_xml(xml, endpoints)
|
319
|
+
endpoints.each do |endpoint|
|
320
|
+
endpoint[:load_balancer] ||= {}
|
321
|
+
protocol = endpoint[:protocol]
|
322
|
+
port = endpoint[:public_port]
|
323
|
+
interval = endpoint[:load_balancer][:interval]
|
324
|
+
timeout = endpoint[:load_balancer][:timeout]
|
325
|
+
path = endpoint[:load_balancer][:path]
|
326
|
+
balancer_name = endpoint[:load_balancer_name]
|
327
|
+
xml.InputEndpoint do
|
328
|
+
xml.LoadBalancedEndpointSetName balancer_name if balancer_name
|
329
|
+
xml.LocalPort endpoint[:local_port]
|
330
|
+
xml.Name endpoint[:name]
|
331
|
+
xml.Port endpoint[:public_port]
|
332
|
+
if balancer_name
|
333
|
+
xml.LoadBalancerProbe do
|
334
|
+
xml.Path path if path
|
335
|
+
xml.Port endpoint[:load_balancer][:port] || port
|
336
|
+
xml.Protocol endpoint[:load_balancer][:protocol] || 'TCP'
|
337
|
+
xml.IntervalInSeconds interval if interval
|
338
|
+
xml.TimeoutInSeconds timeout if timeout
|
339
|
+
end
|
340
|
+
end
|
341
|
+
xml.Protocol protocol
|
342
|
+
xml.EnableDirectServerReturn endpoint[:direct_server_return] unless endpoint[:direct_server_return].nil?
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.add_data_disk_to_xml(lun, media_link, options)
|
348
|
+
if options[:import] && options[:disk_name].nil?
|
349
|
+
Loggerx.error_with_exit "The data disk name is not valid."
|
350
|
+
end
|
351
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
352
|
+
xml.DataVirtualHardDisk(
|
353
|
+
'xmlns' => 'http://schemas.microsoft.com/windowsazure',
|
354
|
+
'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance'
|
355
|
+
) do
|
356
|
+
xml.HostCaching options[:host_caching] || 'ReadOnly'
|
357
|
+
xml.DiskLabel options[:disk_label]
|
358
|
+
xml.DiskName options[:disk_name] if options[:import]
|
359
|
+
xml.Lun lun
|
360
|
+
xml.LogicalDiskSizeInGB options[:disk_size] || 1
|
361
|
+
unless options[:import]
|
362
|
+
disk_name = media_link[/([^\/]+)$/]
|
363
|
+
media_link = media_link.gsub(/#{disk_name}/, (Time.now.strftime('disk_%Y_%m_%d_%H_%M')) + '.vhd')
|
364
|
+
xml.MediaLink media_link
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
builder.doc.to_xml
|
369
|
+
end
|
370
|
+
|
255
371
|
end
|
256
372
|
end
|
257
373
|
end
|
@@ -18,7 +18,6 @@ include Azure::VirtualMachineImageManagement
|
|
18
18
|
module Azure
|
19
19
|
module VirtualMachineManagement
|
20
20
|
class VirtualMachineManagementService < BaseManagementService
|
21
|
-
|
22
21
|
def initialize
|
23
22
|
super()
|
24
23
|
end
|
@@ -26,16 +25,19 @@ module Azure
|
|
26
25
|
# Public: Get a lists of virtual machines available under the current subscription.
|
27
26
|
#
|
28
27
|
# Returns an list of Azure::VirtualMachineManagement::VirtualMachine instances.
|
29
|
-
def list_virtual_machines
|
28
|
+
def list_virtual_machines(*cloud_service_names)
|
30
29
|
roles = []
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
cloud_service_names.flatten!
|
31
|
+
if cloud_service_names.empty?
|
32
|
+
cloud_service = Azure::CloudServiceManagementService.new
|
33
|
+
cloud_service_names = cloud_service.list_cloud_services.map(&:name)
|
34
|
+
end
|
35
|
+
cloud_service_names.each do |cloud_service_name|
|
36
|
+
request_path = "/services/hostedservices/#{cloud_service_name}/deploymentslots/production"
|
35
37
|
request = ManagementHttpRequest.new(:get, request_path)
|
36
38
|
request.warn = true
|
37
39
|
response = request.call
|
38
|
-
roles << Serialization.virtual_machines_from_xml(response,
|
40
|
+
roles << Serialization.virtual_machines_from_xml(response, cloud_service_name)
|
39
41
|
end
|
40
42
|
roles.flatten.compact
|
41
43
|
end
|
@@ -49,7 +51,7 @@ module Azure
|
|
49
51
|
#
|
50
52
|
# Returns an Azure::VirtualMachineManagement::VirtualMachine instance.
|
51
53
|
def get_virtual_machine(name, cloud_service_name)
|
52
|
-
server = list_virtual_machines.select {|x| x.vm_name == name && x.cloud_service_name == cloud_service_name}
|
54
|
+
server = list_virtual_machines(cloud_service_name).select { |x| x.vm_name == name && x.cloud_service_name == cloud_service_name }
|
53
55
|
server.first
|
54
56
|
end
|
55
57
|
|
@@ -83,7 +85,7 @@ module Azure
|
|
83
85
|
# * +:ssh_private_key_file+ - String. Path of private key file.
|
84
86
|
# * +:ssh_certificate_file+ - String. Path of certificate file.
|
85
87
|
# * +:ssh_port+ - Integer. Specifies the SSH port number.
|
86
|
-
# * +:vm_size+ - String. Specifies the size of the virtual machine instance.
|
88
|
+
# * +:vm_size+ - String. Specifies the size of the virtual machine instance.
|
87
89
|
# * +:winrm_transport+ - Array. Specifies WINRM transport protocol.
|
88
90
|
# * +:availability_set_name+ - String. Specifies the availability set name.
|
89
91
|
#
|
@@ -103,15 +105,15 @@ module Azure
|
|
103
105
|
options[:os_type] = get_os_type(params[:image])
|
104
106
|
validate_deployment_params(params, options)
|
105
107
|
options[:deployment_name] ||= options[:cloud_service_name]
|
106
|
-
|
108
|
+
|
107
109
|
unless add_role
|
108
|
-
Loggerx.info
|
110
|
+
Loggerx.info 'Creating deploymnent...'
|
109
111
|
options[:cloud_service_name] ||= generate_cloud_service_name(params[:vm_name])
|
110
112
|
options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name])
|
111
113
|
optionals = {}
|
112
114
|
if options[:virtual_network_name]
|
113
115
|
virtual_network_service = Azure::VirtualNetworkManagementService.new
|
114
|
-
virtual_networks = virtual_network_service.list_virtual_networks.select{|x| x.name == options[:virtual_network_name]}
|
116
|
+
virtual_networks = virtual_network_service.list_virtual_networks.select { |x| x.name == options[:virtual_network_name] }
|
115
117
|
if virtual_networks.empty?
|
116
118
|
Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists"
|
117
119
|
else
|
@@ -124,19 +126,19 @@ module Azure
|
|
124
126
|
end
|
125
127
|
cloud_service = Azure::CloudServiceManagementService.new
|
126
128
|
cloud_service.create_cloud_service(options[:cloud_service_name], optionals)
|
127
|
-
cloud_service.upload_certificate(options[:cloud_service_name],params[:certificate]) unless params[:certificate].empty?
|
129
|
+
cloud_service.upload_certificate(options[:cloud_service_name], params[:certificate]) unless params[:certificate].empty?
|
128
130
|
Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], optionals)
|
129
|
-
body = Serialization.deployment_to_xml(params,options)
|
131
|
+
body = Serialization.deployment_to_xml(params, options)
|
130
132
|
path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments"
|
131
133
|
else
|
132
134
|
|
133
|
-
Loggerx.info
|
135
|
+
Loggerx.info 'Deployment exists, adding role...'
|
134
136
|
body = Serialization.role_to_xml(params, options).to_xml
|
135
137
|
path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments/#{options[:deployment_name]}/roles"
|
136
138
|
end
|
137
139
|
Loggerx.error 'Cloud service name is required for adding role.' unless options[:cloud_service_name]
|
138
140
|
Loggerx.error 'Storage account name is required for adding role.' unless options[:storage_account_name]
|
139
|
-
Loggerx.info
|
141
|
+
Loggerx.info 'Deployment in progress...'
|
140
142
|
request = ManagementHttpRequest.new(:post, path, body)
|
141
143
|
request.call
|
142
144
|
get_virtual_machine(params[:vm_name], options[:cloud_service_name])
|
@@ -156,19 +158,34 @@ module Azure
|
|
156
158
|
#
|
157
159
|
# Returns NONE
|
158
160
|
def delete_virtual_machine(vm_name, cloud_service_name)
|
159
|
-
vm = get_virtual_machine(vm_name,cloud_service_name)
|
161
|
+
vm = get_virtual_machine(vm_name, cloud_service_name)
|
160
162
|
if vm
|
161
163
|
cloud_service = Azure::CloudServiceManagementService.new
|
162
164
|
cloud_service.delete_cloud_service_deployment(cloud_service_name)
|
163
165
|
cloud_service.delete_cloud_service(cloud_service_name)
|
164
166
|
Loggerx.info "Waiting for disk to be released.\n"
|
165
|
-
|
167
|
+
disk_name = vm.disk_name
|
166
168
|
disk_management_service = VirtualMachineDiskManagementService.new
|
167
|
-
|
169
|
+
# Wait for 180s for disk to be released.
|
170
|
+
disk = nil
|
171
|
+
18.times do
|
172
|
+
print '# '
|
173
|
+
disk = disk_management_service.get_virtual_machine_disk(disk_name)
|
174
|
+
unless disk.attached
|
175
|
+
print "Disk released.\n"
|
176
|
+
break
|
177
|
+
end
|
178
|
+
sleep 10
|
179
|
+
end
|
180
|
+
if disk.attached
|
181
|
+
Loggerx.error "\nCannot delete disk #{disk_name}."
|
182
|
+
else
|
183
|
+
disk_management_service.delete_virtual_machine_disk(disk_name)
|
184
|
+
end
|
168
185
|
else
|
169
186
|
Loggerx.error "Cannot find virtual machine #{vm_name} under cloud service #{cloud_service_name}"
|
170
187
|
end
|
171
|
-
rescue
|
188
|
+
rescue
|
172
189
|
end
|
173
190
|
|
174
191
|
# Public: Shuts down the specified virtual machine.
|
@@ -184,8 +201,8 @@ module Azure
|
|
184
201
|
def shutdown_virtual_machine(vm_name, cloud_service_name)
|
185
202
|
vm = get_virtual_machine(vm_name, cloud_service_name)
|
186
203
|
if vm
|
187
|
-
if ['StoppedVM','StoppedDeallocated'].include?(vm.status)
|
188
|
-
Loggerx.error
|
204
|
+
if ['StoppedVM', 'StoppedDeallocated'].include?(vm.status)
|
205
|
+
Loggerx.error 'Cannot perform the shutdown operation on a stopped virtual machine.'
|
189
206
|
elsif vm.deployment_status == 'Running'
|
190
207
|
path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations"
|
191
208
|
body = Serialization.shutdown_virtual_machine_to_xml
|
@@ -193,7 +210,7 @@ module Azure
|
|
193
210
|
request = ManagementHttpRequest.new(:post, path, body)
|
194
211
|
request.call
|
195
212
|
else
|
196
|
-
Loggerx.error
|
213
|
+
Loggerx.error 'Cannot perform the shutdown operation on a stopped deployment.'
|
197
214
|
end
|
198
215
|
else
|
199
216
|
Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\". "
|
@@ -214,7 +231,7 @@ module Azure
|
|
214
231
|
vm = get_virtual_machine(vm_name, cloud_service_name)
|
215
232
|
if vm
|
216
233
|
if vm.status == 'ReadyRole'
|
217
|
-
Loggerx.error
|
234
|
+
Loggerx.error 'Cannot perform the start operation on started virtual machine.'
|
218
235
|
else
|
219
236
|
path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations"
|
220
237
|
body = Serialization.start_virtual_machine_to_xml
|
@@ -227,6 +244,156 @@ module Azure
|
|
227
244
|
end
|
228
245
|
end
|
229
246
|
|
247
|
+
# Public: Restarts the specified virtual machine.
|
248
|
+
#
|
249
|
+
# ==== Attributes
|
250
|
+
#
|
251
|
+
# * +name+ - String. Virtual machine name.
|
252
|
+
# * +cloud_service_name+ - String. Cloud service name.
|
253
|
+
#
|
254
|
+
# See http://msdn.microsoft.com/en-us/library/windowsazure/jj157197.aspx
|
255
|
+
#
|
256
|
+
# Returns NONE
|
257
|
+
def restart_virtual_machine(vm_name, cloud_service_name)
|
258
|
+
vm = get_virtual_machine(vm_name, cloud_service_name)
|
259
|
+
if vm
|
260
|
+
path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations"
|
261
|
+
body = Serialization.restart_virtual_machine_to_xml
|
262
|
+
Loggerx.info "Restarting virtual machine \"#{vm.vm_name}\" ..."
|
263
|
+
request = ManagementHttpRequest.new(:post, path, body)
|
264
|
+
request.call
|
265
|
+
else
|
266
|
+
Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Public: Add/Update endpoints of virtual machine.
|
271
|
+
#
|
272
|
+
# ==== Attributes
|
273
|
+
#
|
274
|
+
# * +name+ - String. Virtual machine name.
|
275
|
+
# * +cloud_service_name+ - String. Cloud service name.
|
276
|
+
# * +input_endpoints+ - Hash. A hash of the name/value pairs for the endpoint.
|
277
|
+
#
|
278
|
+
# ==== Endpoint
|
279
|
+
#
|
280
|
+
# Accepted key/value pairs are:
|
281
|
+
# * +:local_port+ - String. Specifies the internal port on which the
|
282
|
+
# Virtual Machine is listening.
|
283
|
+
# * +:public_port+ - String. Specifies the external port to use for
|
284
|
+
# the endpoint.
|
285
|
+
# * +:name+ - String. Specifies the name of the external endpoint.
|
286
|
+
# * +load_balancer_name+ - String. Specifies a name for a set of
|
287
|
+
# load-balanced endpoints.
|
288
|
+
# * +:protocol+ - String. Specifies the transport protocol
|
289
|
+
# for the endpoint. Possible values are: TCP, UDP
|
290
|
+
# * +:direct_server_return+ - String. Specifies whether the endpoint
|
291
|
+
# uses Direct Server Return. (optional)
|
292
|
+
# * +:load_balancer - Hash. Contains properties that define the
|
293
|
+
# endpoint settings that the load balancer uses to monitor the
|
294
|
+
# availability of the Virtual Machine (optional)
|
295
|
+
#
|
296
|
+
# === Load balancer
|
297
|
+
#
|
298
|
+
# Accepted key/value pairs are:
|
299
|
+
# * +:port+ - String. Specifies the internal port on which the
|
300
|
+
# Virtual Machine is listening.
|
301
|
+
# * +:protocol+ - String. Specifies the protocol to use to inspect the
|
302
|
+
# availability status of the virtual machine.
|
303
|
+
# * +:interval+ - String. Specifies the interval for the load balancer
|
304
|
+
# probe in seconds. (optional)
|
305
|
+
# * +:timeout+ - String. Specifies the timeout for the load balancer
|
306
|
+
# probe in seconds. (optional)
|
307
|
+
# * +:path+ - String. Specifies the relative path to inspect to
|
308
|
+
# determine the availability status of the Virtual Machine. (optional)
|
309
|
+
#
|
310
|
+
# See http://msdn.microsoft.com/en-us/library/windowsazure/jj157187.aspx
|
311
|
+
#
|
312
|
+
# Returns NONE
|
313
|
+
def update_endpoints(vm_name, cloud_service_name, *input_endpoints)
|
314
|
+
input_endpoints.flatten!
|
315
|
+
vm = get_virtual_machine(vm_name, cloud_service_name)
|
316
|
+
if vm
|
317
|
+
path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}"
|
318
|
+
endpoints = vm.tcp_endpoints + vm.udp_endpoints
|
319
|
+
input_endpoints.each do |iep|
|
320
|
+
endpoints.delete_if { |ep| iep[:name].downcase == ep[:name].downcase && iep[:protocol].downcase == ep[:protocol] }
|
321
|
+
end
|
322
|
+
endpoints += input_endpoints
|
323
|
+
body = Serialization.update_role_to_xml(endpoints, vm)
|
324
|
+
request = ManagementHttpRequest.new(:put, path, body)
|
325
|
+
Loggerx.info "Updating endpoints of virtual machine #{vm.vm_name} ..."
|
326
|
+
request.call
|
327
|
+
else
|
328
|
+
Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Public: Delete endpoint of virtual machine.
|
333
|
+
#
|
334
|
+
# ==== Attributes
|
335
|
+
#
|
336
|
+
# * +name+ - String. Virtual machine name.
|
337
|
+
# * +cloud_service_name+ - String. Cloud service name.
|
338
|
+
# * +endpoint_name+ - String. Name of endpoint.
|
339
|
+
#
|
340
|
+
# See http://msdn.microsoft.com/en-us/library/windowsazure/jj157187.aspx
|
341
|
+
#
|
342
|
+
# Returns NONE
|
343
|
+
def delete_endpoint(vm_name, cloud_service_name, endpoint_name)
|
344
|
+
vm = get_virtual_machine(vm_name, cloud_service_name)
|
345
|
+
if vm
|
346
|
+
path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}"
|
347
|
+
endpoints = vm.tcp_endpoints + vm.udp_endpoints
|
348
|
+
endpoints.delete_if { |ep| endpoint_name.downcase == ep[:name].downcase }
|
349
|
+
body = Serialization.update_role_to_xml(endpoints, vm)
|
350
|
+
request = ManagementHttpRequest.new(:put, path, body)
|
351
|
+
Loggerx.info "Deleting virtual machine endpoint #{endpoint_name} ..."
|
352
|
+
request.call
|
353
|
+
else
|
354
|
+
Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# Public: adds a data disk to a virtual machine.
|
359
|
+
#
|
360
|
+
# ==== Attributes
|
361
|
+
#
|
362
|
+
# * +cloud_service_name+ - String. Cloud service name.
|
363
|
+
# * +vm_name+ - String. Virtual machine name.
|
364
|
+
# * +lun+ - String. Specifies the Logical Unit Number
|
365
|
+
# (LUN) for the disk. Valid LUN values are 0 through 15.
|
366
|
+
# * +options+ - Hash. Optional parameters.
|
367
|
+
#
|
368
|
+
# ==== Options
|
369
|
+
#
|
370
|
+
# Accepted key/value pairs in options parameter are:
|
371
|
+
# * +:import+ - Boolean. if true, then allows to use an existing
|
372
|
+
# disk by disk name. if false, then create and attach new data disk.
|
373
|
+
# * +:disk_name+ - String. Specifies the name of the disk.
|
374
|
+
# Reqruied if using existing disk.
|
375
|
+
# * +:host_caching+ - String. Specifies the caching behavior of data disk
|
376
|
+
# The default is ReadOnly. Possible values are: None, ReadOnly, ReadWrite
|
377
|
+
# * +:disk_label+ - String. Specifies the description of the data disk.
|
378
|
+
# * +:disk_size+ - String. Specifies the size of disk in GB
|
379
|
+
#
|
380
|
+
# See http://msdn.microsoft.com/en-us/library/windowsazure/jj157199.aspx
|
381
|
+
#
|
382
|
+
# Returns None
|
383
|
+
def add_data_disk(vm_name, cloud_service_name, lun, options = {})
|
384
|
+
options[:import] ||= false
|
385
|
+
vm = get_virtual_machine(vm_name, cloud_service_name)
|
386
|
+
if vm
|
387
|
+
path = "/services/hostedservices/#{cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}/DataDisks"
|
388
|
+
body = Serialization.add_data_disk_to_xml(lun, vm.media_link, options)
|
389
|
+
Loggerx.info "Adding data disk to virtual machine #{vm_name} ..."
|
390
|
+
request = ManagementHttpRequest.new(:post, path, body)
|
391
|
+
request.call
|
392
|
+
else
|
393
|
+
Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
230
397
|
private
|
231
398
|
|
232
399
|
# Private: Gets the operating system type of an image.
|
@@ -234,45 +401,45 @@ module Azure
|
|
234
401
|
# Returns Linux or Windows
|
235
402
|
def get_os_type(image_name)
|
236
403
|
image_service = Azure::VirtualMachineImageManagementService.new
|
237
|
-
image = image_service.list_virtual_machine_images.select{|x| x.name == image_name}.first
|
238
|
-
Loggerx.error_with_exit
|
404
|
+
image = image_service.list_virtual_machine_images.select { |x| x.name == image_name }.first
|
405
|
+
Loggerx.error_with_exit 'The virtual machine image source is not valid.' unless image
|
239
406
|
image.os_type
|
240
407
|
end
|
241
408
|
|
242
409
|
def generate_cloud_service_name(vm_name)
|
243
|
-
random_string(vm_name+'-service-')
|
410
|
+
random_string(vm_name + '-service-')
|
244
411
|
end
|
245
412
|
|
246
413
|
def generate_storage_account_name(vm_name)
|
247
|
-
random_string(vm_name+'storage').gsub(/[^0-9a-z ]/i, '').downcase[0..23]
|
414
|
+
random_string(vm_name + 'storage').gsub(/[^0-9a-z ]/i, '').downcase[0..23]
|
248
415
|
end
|
249
416
|
|
250
417
|
def validate_deployment_params(params, options)
|
251
418
|
errors = []
|
252
|
-
params_keys = [
|
253
|
-
if options[:os_type] ==
|
254
|
-
params_keys += [
|
419
|
+
params_keys = ['vm_name', 'image', 'location', 'vm_user']
|
420
|
+
if options[:os_type] == 'Windows'
|
421
|
+
params_keys += ['password']
|
255
422
|
end
|
256
423
|
options_keys = []
|
257
|
-
options_keys = ['private_key_file','certificate_file'] if certificate_required?(params, options)
|
424
|
+
options_keys = ['private_key_file', 'certificate_file'] if certificate_required?(params, options)
|
258
425
|
|
259
426
|
params_keys.each do |key|
|
260
427
|
errors << key if params[key.to_sym].nil?
|
261
428
|
end
|
262
|
-
|
429
|
+
|
263
430
|
options_keys.each do |key|
|
264
431
|
errors << key if options[key.to_sym].nil?
|
265
432
|
end
|
266
433
|
validate_role_size(options[:vm_size])
|
267
|
-
validate_location(params[:location]) unless errors.include?(
|
434
|
+
validate_location(params[:location]) unless errors.include?('location')
|
268
435
|
if errors.empty?
|
269
|
-
params[:certificate]={}
|
436
|
+
params[:certificate] = {}
|
270
437
|
if certificate_required?(params, options)
|
271
438
|
begin
|
272
439
|
params[:certificate][:key] = OpenSSL::PKey.read File.read(options[:private_key_file])
|
273
440
|
params[:certificate][:cert] = OpenSSL::X509::Certificate.new File.read(options[:certificate_file])
|
274
441
|
params[:certificate][:fingerprint] = export_fingerprint(params[:certificate][:cert])
|
275
|
-
rescue Exception =>e
|
442
|
+
rescue Exception => e
|
276
443
|
Loggerx.error_with_exit e.message
|
277
444
|
end
|
278
445
|
end
|
@@ -304,14 +471,13 @@ module Azure
|
|
304
471
|
|
305
472
|
def validate_location(location_name)
|
306
473
|
locations = Azure::BaseManagementService.new.list_locations
|
307
|
-
location = locations.select{|loc| loc.name.downcase == location_name.downcase}.first
|
474
|
+
location = locations.select { |loc| loc.name.downcase == location_name.downcase }.first
|
308
475
|
if location.nil?
|
309
|
-
Loggerx.error_with_exit "Value '#{location_name}' specified for parameter 'location' is invalid. Allowed values are #{locations.
|
310
|
-
elsif !location.available_services.include?(
|
476
|
+
Loggerx.error_with_exit "Value '#{location_name}' specified for parameter 'location' is invalid. Allowed values are #{locations.map(&:name).join(',')}"
|
477
|
+
elsif !location.available_services.include?('PersistentVMRole')
|
311
478
|
Loggerx.error_with_exit "Persistentvmrole not enabled for \"#{location.name}\". Try different location"
|
312
479
|
end
|
313
480
|
end
|
314
|
-
|
315
481
|
end
|
316
482
|
end
|
317
483
|
end
|