azure 0.6.1 → 0.6.2
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 +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
|