azure-armrest 0.0.3 → 0.0.4

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.
@@ -7,23 +7,24 @@ module Azure
7
7
  # a corresponding class that wraps the JSON it collects, and each of
8
8
  # them should subclass this base class.
9
9
  class BaseModel < Delegator
10
-
11
- # Declare that a method should return a plain hash instead of an
12
- # OpenStruct instance.
13
- #--
14
- # TODO: Handle nested properties.
15
- def self.hash_properties(method)
16
- define_method(method){ @ostruct[method].to_h }
10
+ def self.excl_list
11
+ # initially inherit the exclusion list from parent class or create an empty Set
12
+ @excl_list ||= superclass.respond_to?(:excl_list, true) ? superclass.send(:excl_list) : Set.new
17
13
  end
14
+ private_class_method :excl_list
18
15
 
19
- hash_properties :tags
16
+ def self.attr_hash(*attrs)
17
+ # merge the declared exclusive attributes to the existing list
18
+ @excl_list = excl_list | Set.new(attrs.map(&:to_s))
19
+ end
20
+ private_class_method :attr_hash
20
21
 
21
- # Access the json instance variable directly.
22
- attr_accessor :json
22
+ attr_hash :tags
23
23
 
24
24
  # Constructs and returns a new JSON wrapper class. Pass in a plain
25
25
  # JSON string and it will automatically give you accessor methods
26
- # that make it behave like a typical Ruby object.
26
+ # that make it behave like a typical Ruby object. You may also pass
27
+ # in a hash.
27
28
  #
28
29
  # Example:
29
30
  # class Person < Azure::ArmRest::BaseModel; end
@@ -40,42 +41,55 @@ module Azure
40
41
  # person.address.zipcode # => '01013'
41
42
  #
42
43
  # # Or you can get back the original JSON if necessary.
43
- # person.json # => Returns original JSON
44
+ # person.to_json # => Returns original JSON
44
45
  #
45
46
  def initialize(json)
46
- @json = json
47
- @resource_group = nil
48
- @ostruct = JSON.parse(json, object_class: OpenStruct)
49
- __setobj__(@ostruct)
47
+ # Find the exclusion list for the model of next level (@embedModel)
48
+ # '#' is the separator between levels. Remove attributes
49
+ # before the first separator.
50
+ child_excl_list = self.class.send(:excl_list).map do |e|
51
+ e.index('#') ? e[e.index('#') + 1 .. -1] : ''
52
+ end
53
+ @embedModel = Class.new(BaseModel) do
54
+ attr_hash *child_excl_list
55
+ end
56
+
57
+ if json.is_a?(Hash)
58
+ hash = json
59
+ @json = json.to_json
60
+ else
61
+ hash = JSON.parse(json)
62
+ @json = json
63
+ end
64
+
65
+ @ostruct = OpenStruct.new(hash)
66
+ super(@ostruct)
50
67
  end
51
68
 
52
- # Return the resource group for the current object.
53
69
  def resource_group
54
70
  @resource_group ||= id[/resourceGroups\/(.+?)\//i, 1] rescue nil
55
71
  end
56
72
 
57
- # Returns the original JSON string passed to the constructor.
73
+ def resource_group=(rg)
74
+ @resource_group = rg
75
+ end
76
+
58
77
  def to_json
59
78
  @json
60
79
  end
61
80
 
62
- # Explicitly convert the object to the original JSON string.
63
81
  def to_s
64
82
  @json
65
83
  end
66
84
 
67
- # Implicitly convert the object to the original JSON string.
68
85
  def to_str
69
86
  @json
70
87
  end
71
88
 
72
- # Custom inspect method that shows the current class and methods.
73
- #--
74
- # TODO: Make this recursive.
75
89
  def inspect
76
90
  string = "<#{self.class} "
77
91
  method_list = methods(false).select{ |m| !m.to_s.include?('=') }
78
- string << method_list.map{ |m| "#{m}=#{send(m)}" }.join(" ")
92
+ string << method_list.map{ |m| "#{m}=#{send(m).inspect}" }.join(", ")
79
93
  string << ">"
80
94
  end
81
95
 
@@ -90,20 +104,22 @@ module Azure
90
104
  # A custom Delegator interface method that creates snake_case
91
105
  # versions of the camelCase delegate methods.
92
106
  def __setobj__(obj)
107
+ excl_list = self.class.send(:excl_list)
93
108
  obj.methods(false).each{ |m|
94
- if m.to_s[-1] != '=' # Must deal with nested ostruct's
109
+ if m.to_s[-1] != '=' && !excl_list.include?(m.to_s) # Must deal with nested models
95
110
  res = obj.send(m)
96
- if res.respond_to?(:each)
97
- res.each{ |o| __setobj__(o) if o.is_a?(OpenStruct) }
98
- else
99
- __setobj__(res) if res.is_a?(OpenStruct)
111
+ if res.is_a?(Array)
112
+ newval = res.map { |elem| elem.is_a?(Hash) ? @embedModel.new(elem) : elem }
113
+ obj.send("#{m}=", newval)
114
+ elsif res.is_a?(Hash)
115
+ obj.send("#{m}=", @embedModel.new(res))
100
116
  end
101
117
  end
102
118
 
103
119
  snake = m.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
104
120
 
105
121
  begin
106
- obj.instance_eval("alias #{snake} #{m}") unless snake == m
122
+ obj.instance_eval("alias #{snake} #{m}; undef :#{m}") unless snake == m
107
123
  rescue SyntaxError
108
124
  next
109
125
  end
@@ -119,8 +135,13 @@ module Azure
119
135
  class ResourceGroup < BaseModel; end
120
136
  class ResourceProvider < BaseModel; end
121
137
  class StorageAccount < BaseModel; end
122
- class TemplateDeployment < BaseModel; end
138
+ class Subscription < BaseModel; end
139
+ class Tag < BaseModel; end
140
+ class TemplateDeployment < BaseModel
141
+ attr_hash 'properties#parameters'
142
+ end
123
143
  class TemplateDeploymentOperation < TemplateDeployment; end
144
+ class Tenant < BaseModel; end
124
145
  class VirtualMachine < BaseModel; end
125
146
  class VirtualMachineInstance < VirtualMachine; end
126
147
  class VirtualMachineModel < VirtualMachine; end
@@ -136,3 +157,5 @@ module Azure
136
157
  end
137
158
  end
138
159
  end
160
+
161
+ require_relative 'storage_account'
@@ -0,0 +1,140 @@
1
+ require 'azure-signature'
2
+ require 'active_support/core_ext/hash/conversions'
3
+ require 'nokogiri'
4
+
5
+ module Azure
6
+ module Armrest
7
+ class StorageAccount < BaseModel
8
+ # Classes used to wrap container and blob information.
9
+ class Container < BaseModel; end
10
+ class Blob < BaseModel; end
11
+ class BlobServiceProperty < BaseModel; end
12
+ class BlobServiceStat < BaseModel; end
13
+ class BlobMetadata < BaseModel; end
14
+
15
+ # The version string used in headers sent as part any internal http
16
+ # request. The default is 2015-02-21.
17
+ attr_accessor :storage_api_version
18
+
19
+ def initialize(json)
20
+ super
21
+ @storage_api_version = '2015-02-21'
22
+ end
23
+
24
+ # Return a list of container names for the given storage account +key+.
25
+ # If no key is provided, it is assumed that the StorageAccount object
26
+ # includes the key1 property.
27
+ #
28
+ def containers(key = nil)
29
+ key ||= properties.key1
30
+
31
+ response = blob_response(key, "comp=list")
32
+
33
+ Nokogiri::XML(response.body).xpath('//Containers/Container').map do |element|
34
+ Container.new(Hash.from_xml(element.to_s)['Container'])
35
+ end
36
+ end
37
+
38
+ # Return a list of blobs for the given +container+ using the given +key+
39
+ # or the key1 property of the StorageAccount object.
40
+ #
41
+ def blobs(container, key = nil)
42
+ key ||= properties.key1
43
+
44
+ url = File.join(properties.primary_endpoints.blob, container)
45
+ url += "?restype=container&comp=list"
46
+
47
+ headers = build_headers(url, key)
48
+ response = RestClient.get(url, headers)
49
+ doc = Nokogiri::XML(response.body)
50
+
51
+ doc.xpath('//Blobs/Blob').map do |node|
52
+ Blob.new(Hash.from_xml(node.to_s)['Blob'])
53
+ end
54
+ end
55
+
56
+ # Returns an array of all blobs for all containers.
57
+ #
58
+ def all_blobs(key = nil)
59
+ key ||= properties.key1
60
+ array = []
61
+ threads = []
62
+
63
+ containers.each do |container|
64
+ threads << Thread.new(container, key){ |c,k| array << blobs(c.name, k) }
65
+ end
66
+
67
+ threads.each(&:join)
68
+
69
+ array.flatten
70
+ end
71
+
72
+ # Returns the blob service properties for the current storage account.
73
+ #
74
+ def blob_properties(key = nil)
75
+ key ||= properties.key1
76
+
77
+ response = blob_response(key, "restype=service&comp=properties")
78
+ toplevel = 'StorageServiceProperties'
79
+
80
+ doc = Nokogiri::XML(response.body).xpath("//#{toplevel}")
81
+ BlobServiceProperty.new(Hash.from_xml(doc.to_s)[toplevel])
82
+ end
83
+
84
+ # Return metadata for the given +blob+ within +container+. You may
85
+ # specify a +date+ to retrieve metadata for a specific snapshot.
86
+ #
87
+ def blob_metadata(container, blob, date = nil, key = nil)
88
+ key ||= properties.key1
89
+
90
+ query = "comp=metadata"
91
+ query << "&snapshot=#{date}" if date
92
+
93
+ response = blob_response(key, query, container, blob)
94
+
95
+ BlobMetadata.new(response.headers)
96
+ end
97
+
98
+ # Retrieves statistics related to replication for the Blob service. Only
99
+ # available on the secondary location endpoint when read-access
100
+ # geo-redundant replication is enabled for the storage account.
101
+ #
102
+ def blob_service_stats(key = nil)
103
+ key ||= properties.key1
104
+
105
+ response = blob_response(key, "restype=service&comp=stats")
106
+ toplevel = 'StorageServiceStats'
107
+
108
+ doc = Nokogiri::XML(response.body).xpath("//#{toplevel}")
109
+ BlobServiceStat.new(Hash.from_xml(doc.to_s)[toplevel])
110
+ end
111
+
112
+ private
113
+
114
+ # Using the blob primary endpoint as a base, join any arguments to the
115
+ # the url and submit an http request.
116
+ #
117
+ def blob_response(key, query, *args)
118
+ url = File.join(properties.primary_endpoints.blob, *args) + "?#{query}"
119
+ headers = build_headers(url, key)
120
+ RestClient.get(url, headers)
121
+ end
122
+
123
+ # Set the headers needed, including the Authorization header.
124
+ #
125
+ def build_headers(url, key)
126
+ sig = Signature.new(url, key)
127
+
128
+ headers = {
129
+ 'x-ms-date' => Time.now.httpdate,
130
+ 'x-ms-version' => @storage_api_version,
131
+ :auth_string => true
132
+ }
133
+
134
+ headers['Authorization'] = sig.blob_signature(headers)
135
+
136
+ headers
137
+ end
138
+ end
139
+ end
140
+ end
@@ -2,102 +2,25 @@ module Azure
2
2
  module Armrest
3
3
  module Network
4
4
  # Class for managing public IP addresss.
5
- class IpAddressService < ArmrestService
5
+ class IpAddressService < ResourceGroupBasedService
6
6
 
7
7
  # Creates and returns a new IpAddressService instance.
8
8
  #
9
9
  def initialize(_armrest_configuration, options = {})
10
10
  super
11
11
  @provider = options[:provider] || 'Microsoft.Network'
12
- set_service_api_version(options, 'publicIPAddresses')
13
- end
14
-
15
- # Return information for the given IP address name for the
16
- # provided +resource_group+. If no group is specified, it will use
17
- # the resource group set in the constructor.
18
- #
19
- # Example:
20
- #
21
- # # Where 'your_ip_name' likely corresponds to a VM name.
22
- # ip.get('your_ip_name', 'your_resource_group')
23
- #
24
- def get(ip_name, resource_group = armrest_configuration.resource_group)
25
- raise ArgumentError, "must specify resource group" unless resource_group
26
- url = build_url(resource_group, ip_name)
27
- JSON.parse(rest_get(url))
12
+ @service_name = 'publicIPAddresses'
13
+ set_service_api_version(options, @service_name)
28
14
  end
29
15
 
30
16
  # Shortcut method that returns just the IP address for the given public
31
17
  # IP address name.
32
18
  #
33
19
  def get_ip(ip_name, resource_group = armrest_configuration.resource_group)
34
- get(ip_name, resource_group)['properties']['ipAddress']
35
- end
36
-
37
- # Returns a list of available IP addresss for the given subscription
38
- # for the provided +group+, or for all resource groups if no group is specified.
39
- #
40
- def list(group = nil)
41
- if group
42
- url = build_url(group)
43
- JSON.parse(rest_get(url))['value']
44
- else
45
- array = []
46
- threads = []
47
- mutex = Mutex.new
48
-
49
- resource_groups.each do |rg|
50
- threads << Thread.new(rg['name']) do |group|
51
- url = build_url(group)
52
- response = rest_get(url)
53
- results = JSON.parse(response)['value']
54
- if results && !results.empty?
55
- mutex.synchronize{
56
- results.each{ |hash| hash['resourceGroup'] = group }
57
- array << results
58
- }
59
- end
60
- end
61
- end
62
-
63
- threads.each(&:join)
64
-
65
- array.flatten
66
- end
67
- end
68
-
69
- # List all IP addresss for the current subscription.
70
- #
71
- def list_all_for_subscription
72
- sub_id = armrest_configuration.subscription_id
73
- url = File.join(
74
- Azure::Armrest::COMMON_URI, sub_id, 'providers',
75
- @provider, 'publicIPAddresses'
76
- )
77
- url << "?api-version=#{@api_version}"
78
- JSON.parse(rest_get(url))['value']
20
+ get(ip_name, resource_group).properties.ipAddress
79
21
  end
80
22
 
81
- alias list_all list_all_for_subscription
82
-
83
- private
84
-
85
- # Builds a URL based on subscription_id an resource_group and any other
86
- # arguments provided, and appends it with the api-version.
87
- def build_url(resource_group, *args)
88
- url = File.join(
89
- Azure::Armrest::COMMON_URI,
90
- armrest_configuration.subscription_id,
91
- 'resourceGroups',
92
- resource_group,
93
- 'providers',
94
- @provider,
95
- 'publicIPAddresses',
96
- )
97
-
98
- url = File.join(url, *args) unless args.empty?
99
- url << "?api-version=#{@api_version}"
100
- end
23
+ alias get_ip_address get_ip
101
24
  end
102
25
  end # Network
103
26
  end # Armrest
@@ -2,121 +2,14 @@ module Azure
2
2
  module Armrest
3
3
  module Network
4
4
  # Class for managing network interfaces.
5
- class NetworkInterfaceService < ArmrestService
5
+ class NetworkInterfaceService < ResourceGroupBasedService
6
6
  # Creates and returns a new NetworkInterfaceService instance.
7
7
  #
8
8
  def initialize(_armrest_configuration, options = {})
9
9
  super
10
10
  @provider = options[:provider] || 'Microsoft.Network'
11
- set_service_api_version(options, 'networkInterfaces')
12
- end
13
-
14
- # Return information for the given network interface card for the
15
- # provided +resource_group+. If no group is specified, it will use the
16
- # resource group set in the constructor.
17
- #
18
- # Example:
19
- #
20
- # nsg.get('your_interface', 'your_resource_group')
21
- #
22
- def get(interface, resource_group = armrest_configuration.resource_group)
23
- raise ArgumentError, "must specify resource group" unless resource_group
24
- url = build_url(resource_group, interface)
25
- JSON.parse(rest_get(url))
26
- end
27
-
28
- # Returns a list of available network interfaces in the current subscription
29
- # for the provided +resource_group+.
30
- #
31
- def list(resource_group = armrest_configuration.resource_group)
32
- raise ArgumentError, "no resource group provided" unless resource_group
33
- url = build_url(resource_group)
34
- JSON.parse(rest_get(url))['value']
35
- end
36
-
37
- # List all network interfaces for the current subscription.
38
- #
39
- def list_all_for_subscription
40
- sub_id = armrest_configuration.subscription_id
41
- url = File.join(Azure::Armrest::COMMON_URI, sub_id, 'providers', @provider, 'networkInterfaces')
42
- url << "?api-version=#{@api_version}"
43
- JSON.parse(rest_get(url))['value']
44
- end
45
-
46
- alias list_all list_all_for_subscription
47
-
48
- # Delete the given network interface in +resource_group+.
49
- #
50
- def delete(interface, resource_group = armrest_configuration.resource_group)
51
- raise ArgumentError, "must specify resource group" unless resource_group
52
-
53
- url = build_url(resource_group, interface)
54
- response = rest_delete(url)
55
- response.return!
56
- end
57
-
58
- # Create a new network interface, or update an existing network interface if it
59
- # already exists. The first argument is a hash of options.
60
- #
61
- # - :name # Mandatory
62
- # - :location # Mandatory
63
- # - :tags
64
- # - :properties
65
- # - :networkSecurityGroup
66
- # - :id
67
- # - :ipConfigurations
68
- # [
69
- # - :name # Mandatory
70
- # - :properties
71
- # - :subnet
72
- # - :id # Mandatory
73
- # - :privateIPAddress
74
- # - :privateIPAllocationMethod # Mandatory
75
- # - :publicIPAddress
76
- # - :id
77
- # - :loadBalancerBackendAddressPools
78
- # - :id
79
- # - :loadBalancerInboundNatRules
80
- # - :id
81
- # ]
82
- #
83
- # - :dnsSettings
84
- # - :dnsServers[ <ip1>, <ip2>, ... ]
85
- #
86
- # For convenience, you may also pass the :resource_group as a hash option.
87
- #
88
- def create(name, options, resource_group = armrest_configuration.resource_group)
89
- resource_group = options.delete(:resource_group) || resource_group
90
-
91
- raise ArgumentError, "no resource group specified" unless resource_group
92
-
93
- body = options.to_json
94
-
95
- url = build_url(resource_group, name)
96
-
97
- response = rest_put(url, body)
98
- response.return!
99
- end
100
-
101
- alias update create
102
-
103
- private
104
-
105
- # Builds a URL based on subscription_id an resource_group and any other
106
- # arguments provided, and appends it with the api-version.
107
- def build_url(resource_group, *args)
108
- url = File.join(
109
- Azure::Armrest::COMMON_URI,
110
- armrest_configuration.subscription_id,
111
- 'resourceGroups',
112
- resource_group,
113
- 'providers',
114
- @provider,
115
- 'networkInterfaces',
116
- )
117
-
118
- url = File.join(url, *args) unless args.empty?
119
- url << "?api-version=#{@api_version}"
11
+ @service_name = 'networkInterfaces'
12
+ set_service_api_version(options, @service_name)
120
13
  end
121
14
  end
122
15
  end # Network