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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 16b612bbee697e6b52ae74452eb8c74474582c95
4
- data.tar.gz: c54be6fa7ef63f2867b680ad272ae5745e62b51b
3
+ metadata.gz: 2218a9a37d85a587e477daa5f91d8c06d0eb3f59
4
+ data.tar.gz: 39efa8b58227090e87058d9aa4765251d423f4ce
5
5
  SHA512:
6
- metadata.gz: ac0c2777f58a96237eb85db41f6990932b352d1873aa8c7bd256b9422ed4ab760ce590618006f517b2d56baf4cb9376b6288929a9093f01a7ebe464d0edaa9d2
7
- data.tar.gz: 7fe4e38feb67a395d7a7e986d3415cb434f13857298ccb27b09356485d46a447165bf6c006c00e5b600e41c2a96ada8877b07aea16c0d14fdc4a1832045f7ec2
6
+ metadata.gz: a47b93b9e6e87ea04bc0458569e16d3b75046f8a3ce178c4d0914078f94d4b78d4ffd3ac124c8870033f09cf2d6932fec147ed739d43f1fb71e8cb72620a32ca
7
+ data.tar.gz: f15f40f69a33d35902b0f585c1de2a405a7468b7c21befe2b0902d0d61da233923c8bb417b4d0212e8ddab87937a9607c32d13220e2316e9d492df2d2a314c40
@@ -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
 
@@ -17,7 +17,7 @@ module Azure
17
17
  class Version
18
18
  MAJOR = 0 unless defined? MAJOR
19
19
  MINOR = 6 unless defined? MINOR
20
- UPDATE = 1 unless defined? UPDATE
20
+ UPDATE = 2 unless defined? UPDATE
21
21
  PRE = nil unless defined? PRE
22
22
 
23
23
  class << self
@@ -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
@@ -20,7 +20,7 @@ module Azure
20
20
  yield self if block_given?
21
21
  end
22
22
 
23
- attr_accessor :name, :attached
23
+ attr_accessor :name, :attached, :os_type, :image, :size
24
24
 
25
25
  end
26
26
  end
@@ -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 params[:certificate][:fingerprint]
126
+ if fingerprint
114
127
  xml.SSH do
115
128
  xml.PublicKeys do
116
129
  xml.PublicKey do
117
- xml.Fingerprint params[:certificate][: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 params[:certificate][:fingerprint] if params[:certificate][:fingerprint]
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
- xml.InputEndpoint do
157
- xml.LocalPort '22'
158
- xml.Name 'SSH'
159
- xml.Port options[:ssh_port] || '22'
160
- xml.Protocol 'TCP'
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
- elsif os_type == 'Windows'
163
- if options[:winrm_transport] && options[:winrm_transport].include?('http')
164
- xml.InputEndpoint do
165
- xml.LocalPort '5985'
166
- xml.Name 'WinRm-Http'
167
- xml.Port '5985'
168
- xml.Protocol 'TCP'
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
- if tcp_endpoints
184
- tcp_endpoints.split(',').each do |endpoint|
185
- ports = endpoint.split(':')
186
- xml.InputEndpoint do
187
- xml.LocalPort ports[0]
188
- if ports.length > 1
189
- xml.Name 'TCP-PORT-' + ports[1]
190
- xml.Port ports[1]
191
- else
192
- xml.Name 'TCP-PORT-' + ports[0]
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 (deployXML.nil? or deployXML.at_css('Deployment Name').nil?)
203
- rolesXML = deployXML.css('Deployment RoleInstanceList RoleInstance')
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
- rolesXML.each do |instance|
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 = xml_content(instance, 'RoleName').downcase
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
- tcp_endpoints_from_xml(instance, vm)
215
- vm.ipaddress = xml_content(instance, 'IpAddress') unless vm.ipaddress
216
- vm.virtual_network_name = xml_content(deployXML.css('Deployment'), 'VirtualNetworkName')
217
- deployXML.css('Deployment RoleList Role').each do |role|
218
- if xml_content(role, 'RoleName') == xml_content(instance, 'RoleName')
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.tcp_endpoints_from_xml(rolesXML, vm)
253
+ def self.endpoints_from_xml(rolesXML, vm)
232
254
  vm.tcp_endpoints = []
233
255
  vm.udp_endpoints = []
234
- endpoints = rolesXML.css('InstanceEndpoint')
256
+ endpoints = rolesXML.css('ConfigurationSets ConfigurationSet InputEndpoints InputEndpoint')
235
257
  endpoints.each do |endpoint|
236
- if vm.ipaddress.nil?
237
- if xml_content(endpoint, 'Name').downcase == 'ssh'
238
- vm.ipaddress = xml_content(endpoint, 'Vip')
239
- elsif !(xml_content(endpoint, 'Name').downcase =~ /winrm/).nil?
240
- vm.ipaddress = xml_content(endpoint, 'Vip')
241
- end
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
- hash = Hash.new
244
- hash['Name'] = xml_content(endpoint, 'Name')
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 << hash
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
@@ -36,6 +36,7 @@ module Azure
36
36
  attr_accessor :disk_name
37
37
  attr_accessor :virtual_network_name
38
38
  attr_accessor :availability_set_name
39
+ attr_accessor :media_link
39
40
  end
40
41
  end
41
42
  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
- cloud_service = Azure::CloudServiceManagementService.new
32
- cloud_services = cloud_service.list_cloud_services
33
- cloud_services.each do |cloud_service|
34
- request_path = "/services/hostedservices/#{cloud_service.name}/deploymentslots/production"
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,cloud_service.name)
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 "Creating deploymnent..."
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 "Deployment exists, adding role..."
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 "Deployment in progress..."
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
- sleep 60
167
+ disk_name = vm.disk_name
166
168
  disk_management_service = VirtualMachineDiskManagementService.new
167
- disk_management_service.delete_virtual_machine_disk(vm.disk_name)
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 "Cannot perform the shutdown operation on a stopped virtual machine."
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 "Cannot perform the shutdown operation on a stopped deployment."
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 "Cannot perform the start operation on started virtual machine."
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 "The virtual machine image source is not valid." unless image
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 = ["vm_name", "image", "location", "vm_user"]
253
- if options[:os_type] == "Windows"
254
- params_keys += ["password"]
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?("location")
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.collect(&:name).join(',')}"
310
- elsif !location.available_services.include?("PersistentVMRole")
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