azure-armrest 0.3.11 → 0.3.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 374a82fa5abab25e86c1d55475f91cbcd51f0606
4
- data.tar.gz: 8fb3f44d22dbe3f8d4697c74205d671e04c7be96
3
+ metadata.gz: 5022f64bbe2cfc12d269a9b1ef34eda423076aff
4
+ data.tar.gz: 86f456d2c2e7eb2724599da6d2239f3dc1839e7b
5
5
  SHA512:
6
- metadata.gz: 67130daca26bbfc9b629dd256f65a92412eaebe7b59a832a4f80b032ae0622052c4979640631458609fb466aac204e1f0f678184d5ed13e2e45a69085b885b9d
7
- data.tar.gz: 2539f429201b3d24ad4703bab06b4e7112125dba85aa29a047ab6c1d0843a9a13eaf2ff229b9850ff34695d6b7412c694d3d63bf36d2f493dcff7c4b7ccada96
6
+ metadata.gz: 516af488cfffe832df8e468c19201ab59253130023d0eddc1c0bba2dbe6ddb269b51ff781f5218c6090a37450d8d057f5ccf0083b737941c2830e67988a32f2b
7
+ data.tar.gz: 2ef615e16a7809c5418becdcf17439c9303a3f05119c2a26925319ba16194f028e7599537fb4c940b6a29d0b3833e24a89e05d2aff67fb09f11ae58c978fd16e
data/CHANGES CHANGED
@@ -1,3 +1,12 @@
1
+ = 0.3.12 - 15-Nov-2016
2
+ * Added the Insights::DiagnosticService class.
3
+ * Added the get_from_vm and get_os_disk methods to StorageAccountService.
4
+ * Added the delete_associated_resources method to VirtualMachineService.
5
+ * The output of StorageAccount#blob_properties method now includes the blob
6
+ name and the container name.
7
+ * Added a service_name accessor to the ArmrestService base class.
8
+ * The list_all method in StorageAccountService now supports a filter.
9
+
1
10
  = 0.3.11 - 8-Nov-2016
2
11
  * Fixed the regex for the internal method parse_id_string, making the
3
12
  get_associated_resource method more robust.
data/lib/azure/armrest.rb CHANGED
@@ -39,6 +39,7 @@ require 'azure/armrest/resource_service'
39
39
  require 'azure/armrest/resource_group_service'
40
40
  require 'azure/armrest/resource_provider_service'
41
41
  require 'azure/armrest/insights/alert_service'
42
+ require 'azure/armrest/insights/diagnostic_service'
42
43
  require 'azure/armrest/insights/event_service'
43
44
  require 'azure/armrest/insights/metrics_service'
44
45
  require 'azure/armrest/network/load_balancer_service'
@@ -18,6 +18,9 @@ module Azure
18
18
  # Provider for service specific API calls
19
19
  attr_accessor :provider
20
20
 
21
+ # The service name for the Service class
22
+ attr_accessor :service_name
23
+
21
24
  # The api-version string for this particular service
22
25
  attr_accessor :api_version
23
26
 
@@ -0,0 +1,96 @@
1
+ module Azure
2
+ module Armrest
3
+ module Insights
4
+ # Base class for managing diagnostics
5
+ class DiagnosticService < ArmrestService
6
+ def initialize(armrest_configuration, options = {})
7
+ super(armrest_configuration, 'diagnosticSettings', 'Microsoft.Insights', options)
8
+ end
9
+
10
+ # Get diagnostic information for the given +resource_id+. Note that
11
+ # this information is only available for a limited subset of resources.
12
+ #
13
+ # Example:
14
+ #
15
+ # ids = Azure::Armrest::Insights::DiagnosticService.new(config)
16
+ # nsg = Azure::Armrest::Network::NetworkSecurityGroupService.new(config)
17
+ #
18
+ # sgrp = nsg.get(your_security_group, your_resource_group)
19
+ # p ids.get(sgrp.id)
20
+ #
21
+ def get(resource_id)
22
+ url = build_url(resource_id)
23
+ response = rest_get(url)
24
+ Diagnostic.new(JSON.parse(response))
25
+ end
26
+
27
+ # Create or update a diagnostic setting for the given +resource_id+.
28
+ #
29
+ # Example:
30
+ #
31
+ # # Update network security group log settings
32
+ # ids = Azure::Armrest::Insights::DiagnosticService.new(config)
33
+ # sas = Azure::Armrest::StorageAccountService.new(config)
34
+ # nsg = Azure::Armrest::Network::NetworkSecurityGroupService.new(config)
35
+ #
36
+ # acct = sas.get(your_storage, your_resource_group)
37
+ # sgrp = nsg.get(your_network_security_group, your_resource_group)
38
+ #
39
+ # options = {
40
+ # :properties => {
41
+ # :storageAccountId => acct.id,
42
+ # :logs => [
43
+ # {
44
+ # :category => "NetworkSecurityGroupEvent",
45
+ # :enabled => true,
46
+ # :retentionPolicy => {
47
+ # :enabled => true,
48
+ # :days => 3
49
+ # }
50
+ # },
51
+ # {
52
+ # :category => "NetworkSecurityGroupRuleCounter",
53
+ # :enabled => true,
54
+ # :retentionPolicy => {
55
+ # :enabled => true,
56
+ # :days => 3
57
+ # }
58
+ # }
59
+ # ]
60
+ # }
61
+ # }
62
+ #
63
+ # ids.set(sgrp.id, options)
64
+ #
65
+ def create(resource_id, options = {})
66
+ url = build_url(resource_id)
67
+ body = options.merge(:id => resource_id).to_json
68
+ response = rest_put(url, body)
69
+
70
+ headers = Azure::Armrest::ResponseHeaders.new(response.headers)
71
+ headers.response_code = response.code
72
+
73
+ headers
74
+ end
75
+
76
+ alias update create
77
+ alias set create
78
+
79
+ private
80
+
81
+ def build_url(resource_id)
82
+ url = File.join(
83
+ Azure::Armrest::RESOURCE,
84
+ resource_id,
85
+ 'providers',
86
+ provider,
87
+ 'diagnosticSettings',
88
+ 'service'
89
+ )
90
+
91
+ url + "?api-version=#{api_version}"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -218,6 +218,7 @@ module Azure
218
218
 
219
219
  module Insights
220
220
  class Alert < BaseModel; end
221
+ class Diagnostic < BaseModel; end
221
222
  class Event < BaseModel; end
222
223
  class Metric < BaseModel; end
223
224
  end
@@ -214,7 +214,7 @@ module Azure
214
214
  :ssl_verify => ssl_verify
215
215
  )
216
216
 
217
- BlobProperty.new(response.headers)
217
+ BlobProperty.new(response.headers.merge(:container => container, :name => blob))
218
218
  end
219
219
 
220
220
  # Return a list of blobs for the given +container+ using the given +key+
@@ -32,8 +32,8 @@ module Azure
32
32
 
33
33
  # Same as other resource based list_all methods, but also sets the proxy on each model object.
34
34
  #
35
- def list_all
36
- super.each do |m|
35
+ def list_all(filter = {})
36
+ super(filter).each do |m|
37
37
  m.proxy = configuration.proxy
38
38
  m.ssl_version = configuration.ssl_version
39
39
  m.ssl_verify = configuration.ssl_verify
@@ -203,6 +203,43 @@ module Azure
203
203
  get_private_images(storage_accounts)
204
204
  end
205
205
 
206
+ # Return the storage account for the virtual machine model +vm+.
207
+ #
208
+ def get_from_vm(vm)
209
+ uri = Addressable::URI.parse(vm.properties.storage_profile.os_disk.vhd.uri)
210
+
211
+ # The uri looks like https://foo123.blob.core.windows.net/vhds/something123.vhd
212
+ name = uri.host.split('.').first # storage name, e.g. 'foo123'
213
+
214
+ # Look for the storage account in the VM's resource group first. If
215
+ # it's not found, look through all the storage accounts.
216
+ begin
217
+ acct = get(name, vm.resource_group)
218
+ rescue Azure::Armrest::NotFoundException => err
219
+ acct = list_all(:name => name).first
220
+ raise err unless acct
221
+ end
222
+
223
+ acct
224
+ end
225
+
226
+ # Get information for the underlying VHD file based on the properties
227
+ # of the virtual machine model +vm+.
228
+ #
229
+ def get_os_disk(vm)
230
+ uri = Addressable::URI.parse(vm.properties.storage_profile.os_disk.vhd.uri)
231
+
232
+ # The uri looks like https://foo123.blob.core.windows.net/vhds/something123.vhd
233
+ disk = File.basename(uri.to_s) # disk name, e.g. 'something123.vhd'
234
+ path = File.dirname(uri.path)[1..-1] # container, e.g. 'vhds'
235
+
236
+ acct = get_from_vm(vm)
237
+ keys = list_account_keys(acct.name, acct.resource_group)
238
+ key = keys['key1'] || keys['key2']
239
+
240
+ acct.blob_properties(path, disk, key)
241
+ end
242
+
206
243
  def accounts_by_name
207
244
  @accounts_by_name ||= list_all.each_with_object({}) { |sa, sah| sah[sa.name] = sa }
208
245
  end
@@ -1,5 +1,5 @@
1
1
  module Azure
2
2
  module Armrest
3
- VERSION = '0.3.11'.freeze
3
+ VERSION = '0.3.12'.freeze
4
4
  end
5
5
  end
@@ -121,12 +121,173 @@ module Azure
121
121
  vm_operate('powerOff', vmname, group)
122
122
  end
123
123
 
124
+ # Delete the VM and associated resources. By default, this will
125
+ # delete the VM, its NIC, the associated IP address, and the
126
+ # image files (.vhd and .status) for the VM.
127
+ #
128
+ # If you want to delete other associated resources, such as any
129
+ # attached disks, the VM's underlying storage account, or associated
130
+ # network security groups you must explicitly specify them as an option.
131
+ #
132
+ # An attempt to delete a resource that cannot be deleted because it's
133
+ # still associated with some other resource will be logged and skipped.
134
+ #
135
+ # If the :verbose option is set to true, then additional messages are
136
+ # sent to your configuration log, or stdout if no log was specified.
137
+ #
138
+ # Note that if all of your related resources are in a self-contained
139
+ # resource group, you do not necessarily need this method. You could
140
+ # just delete the resource group itself, which would automatically
141
+ # delete all of its resources.
142
+ #
143
+ def delete_associated_resources(vmname, vmgroup, options = {})
144
+ options = {
145
+ :network_interfaces => true,
146
+ :ip_addresses => true,
147
+ :os_disk => true,
148
+ :network_security_groups => false,
149
+ :storage_account => false,
150
+ :verbose => false
151
+ }.merge(options)
152
+
153
+ Azure::Armrest::Configuration.log ||= STDOUT if options[:verbose]
154
+
155
+ vm = get(vmname, vmgroup)
156
+
157
+ delete_and_wait(self, vmname, vmgroup, options)
158
+
159
+ # Must delete network interfaces first if you want to delete
160
+ # IP addresses or network security groups.
161
+ if options[:network_interfaces] || options[:ip_addresses] || options[:network_security_groups]
162
+ delete_associated_nics(vm, options)
163
+ end
164
+
165
+ if options[:os_disk] || options[:storage_account]
166
+ delete_associated_disk(vm, options)
167
+ end
168
+ end
169
+
124
170
  def model_class
125
171
  VirtualMachineModel
126
172
  end
127
173
 
128
174
  private
129
175
 
176
+ # Deletes any NIC's associated with the VM, and optionally any public IP addresses
177
+ # and network security groups.
178
+ #
179
+ def delete_associated_nics(vm, options)
180
+ nis = Azure::Armrest::Network::NetworkInterfaceService.new(configuration)
181
+ nics = vm.properties.network_profile.network_interfaces.map(&:id)
182
+
183
+ if options[:ip_addresses]
184
+ ips = Azure::Armrest::Network::IpAddressService.new(configuration)
185
+ end
186
+
187
+ if options[:network_security_groups]
188
+ nsgs = Azure::Armrest::Network::NetworkSecurityGroupService.new(configuration)
189
+ end
190
+
191
+ nics.each do |nic_id_string|
192
+ nic = get_associated_resource(nic_id_string)
193
+ delete_and_wait(nis, nic.name, nic.resource_group, options)
194
+
195
+ if options[:ip_addresses]
196
+ nic.properties.ip_configurations.each do |ip|
197
+ ip = get_associated_resource(ip.properties.public_ip_address.id)
198
+ delete_and_wait(ips, ip.name, ip.resource_group, options)
199
+ end
200
+ end
201
+
202
+ if options[:network_security_groups]
203
+ nic.properties.network_security_group
204
+ nsg = get_associated_resource(nic.properties.network_security_group.id)
205
+ delete_and_wait(nsgs, nsg.name, nsg.resource_group, options)
206
+ end
207
+ end
208
+ end
209
+
210
+ # This deletes the OS disk from the storage account that's backing the
211
+ # virtual machine, along with the .status file. This does NOT delete
212
+ # copies of the disk.
213
+ #
214
+ # If the option to delete the entire storage account was selected, then
215
+ # it will not bother with deleting invidual files from the storage
216
+ # account first.
217
+ #
218
+ def delete_associated_disk(vm, options)
219
+ sas = Azure::Armrest::StorageAccountService.new(configuration)
220
+
221
+ storage_account = sas.get_from_vm(vm)
222
+
223
+ # Deleting the storage account does not require deleting the disks
224
+ # first, so skip that if deletion of the storage account was requested.
225
+ if options[:storage_account]
226
+ delete_and_wait(sas, storage_account.name, storage_account.resource_group, options)
227
+ else
228
+ keys = sas.list_account_keys(storage_account.name, storage_account.resource_group)
229
+ key = keys['key1'] || keys['key2']
230
+ disk = sas.get_os_disk(vm)
231
+
232
+ # There's a short delay between deleting the VM and unlocking the underlying
233
+ # .vhd file by Azure. Therefore we sleep up to two minutes while checking.
234
+ if disk.x_ms_lease_status.casecmp('unlocked') != 0
235
+ sleep_time = 0
236
+
237
+ while sleep_time < 120
238
+ sleep 10
239
+ sleep_time += 10
240
+ disk = sas.get_os_disk(vm)
241
+ break if disk.x_ms_lease_status.casecmp('unlocked') != 0
242
+ end
243
+
244
+ # In the unlikely event it did not unlock, just log and skip.
245
+ if disk.x_ms_lease_status.casecmp('unlocked') != 0
246
+ log_message("Unable to delete disk #{disk.container}/#{disk.name}", 'warn')
247
+ return
248
+ end
249
+ end
250
+
251
+ storage_account.delete_blob(disk.container, disk.name, key)
252
+ log_message("Deleted blob #{disk.container}/#{disk.name}") if options[:verbose]
253
+
254
+ begin
255
+ status_file = File.basename(disk.name, '.vhd') + '.status'
256
+ storage_account.delete_blob(disk.container, status_file, key)
257
+ rescue Azure::Armrest::NotFoundException
258
+ # Ignore, does not always exist.
259
+ else
260
+ log_message("Deleted blob #{disk.container}/#{status_file}") if options[:verbose]
261
+ end
262
+ end
263
+ end
264
+
265
+ # Delete a +service+ type resource using its name and resource group,
266
+ # and wait for the operation to complete before returning.
267
+ #
268
+ # If the operation fails because a dependent resource is still attached,
269
+ # then the error is logged (in verbose mode) and ignored.
270
+ #
271
+ def delete_and_wait(service, name, group, options)
272
+ resource_type = service.class.to_s.sub('Service', '').split('::').last
273
+
274
+ log_message("Deleting #{resource_type} #{name}/#{group}") if options[:verbose]
275
+
276
+ headers = service.delete(name, group)
277
+
278
+ loop do
279
+ status = wait(headers)
280
+ break if status.downcase.start_with?('succ') # Succeeded, Success, etc.
281
+ end
282
+
283
+ log_message("Deleted #{resource_type} #{name}/#{group}") if options[:verbose]
284
+ rescue Azure::Armrest::BadRequestException, Azure::Armrest::PreconditionFailedException => err
285
+ if options[:verbose]
286
+ msg = "Unable to delete #{resource_type} #{name}/#{group}, skipping. Message: #{err.message}"
287
+ log_message(msg, 'warn')
288
+ end
289
+ end
290
+
130
291
  def vm_operate(action, vmname, group, options = {})
131
292
  raise ArgumentError, "must specify resource group" unless group
132
293
  raise ArgumentError, "must specify name of the vm" unless vmname
@@ -135,6 +296,15 @@ module Azure
135
296
  rest_post(url)
136
297
  nil
137
298
  end
299
+
300
+ # Simple log messager. Use the Configuration.log if defined.
301
+ def log_message(msg, level = 'info')
302
+ if Azure::Armrest::Configuration.log
303
+ Azure::Armrest::Configuration.log.send(level.to_sym, msg)
304
+ else
305
+ warn msg
306
+ end
307
+ end
138
308
  end
139
309
  end
140
310
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure-armrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.11
4
+ version: 0.3.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-11-08 00:00:00.000000000 Z
14
+ date: 2016-11-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: json
@@ -240,6 +240,7 @@ files:
240
240
  - lib/azure/armrest/configuration.rb
241
241
  - lib/azure/armrest/exception.rb
242
242
  - lib/azure/armrest/insights/alert_service.rb
243
+ - lib/azure/armrest/insights/diagnostic_service.rb
243
244
  - lib/azure/armrest/insights/event_service.rb
244
245
  - lib/azure/armrest/insights/metrics_service.rb
245
246
  - lib/azure/armrest/model/base_model.rb