misty 1.1.0 → 1.2.0

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/lib/misty/auth.rb +5 -13
  3. data/lib/misty/auth/auth_v2.rb +13 -11
  4. data/lib/misty/auth/auth_v3.rb +11 -9
  5. data/lib/misty/auth/errors.rb +12 -0
  6. data/lib/misty/auth/name.rb +4 -4
  7. data/lib/misty/client.rb +105 -0
  8. data/lib/misty/client_pack.rb +16 -0
  9. data/lib/misty/http/method_builder.rb +36 -20
  10. data/lib/misty/misty.rb +14 -0
  11. data/lib/misty/openstack/aodh/aodh_v2.rb +1 -1
  12. data/lib/misty/openstack/aodh/v2.rb +5 -8
  13. data/lib/misty/openstack/ceilometer/ceilometer_v2.rb +1 -1
  14. data/lib/misty/openstack/ceilometer/v2.rb +5 -8
  15. data/lib/misty/openstack/cinder/cinder_v1.rb +1 -1
  16. data/lib/misty/openstack/cinder/cinder_v2.rb +1 -1
  17. data/lib/misty/openstack/cinder/cinder_v3.rb +1 -1
  18. data/lib/misty/openstack/cinder/v1.rb +6 -9
  19. data/lib/misty/openstack/cinder/v2.rb +6 -9
  20. data/lib/misty/openstack/cinder/v3.rb +7 -10
  21. data/lib/misty/openstack/designate/designate_v2.rb +1 -1
  22. data/lib/misty/openstack/designate/v2.rb +5 -8
  23. data/lib/misty/openstack/extension.rb +33 -0
  24. data/lib/misty/openstack/freezer/freezer_v1.rb +1 -1
  25. data/lib/misty/openstack/freezer/v1.rb +5 -8
  26. data/lib/misty/openstack/glance/glance_v1.rb +1 -1
  27. data/lib/misty/openstack/glance/glance_v2.rb +1 -1
  28. data/lib/misty/openstack/glance/v1.rb +5 -8
  29. data/lib/misty/openstack/glance/v2.rb +5 -8
  30. data/lib/misty/openstack/heat/heat_v1.rb +1 -1
  31. data/lib/misty/openstack/heat/v1.rb +6 -9
  32. data/lib/misty/openstack/ironic/ironic_v1.rb +1 -1
  33. data/lib/misty/openstack/ironic/v1.rb +6 -9
  34. data/lib/misty/openstack/karbor/karbor_v1.rb +1 -1
  35. data/lib/misty/openstack/karbor/v1.rb +5 -8
  36. data/lib/misty/openstack/keystone/keystone_v2_0.rb +1 -1
  37. data/lib/misty/openstack/keystone/keystone_v2_0_ext.rb +2 -2
  38. data/lib/misty/openstack/keystone/keystone_v3.rb +1 -1
  39. data/lib/misty/openstack/keystone/keystone_v3_ext.rb +2 -2
  40. data/lib/misty/openstack/keystone/v2_0.rb +8 -10
  41. data/lib/misty/openstack/keystone/v3.rb +8 -10
  42. data/lib/misty/openstack/magnum/magnum_v1.rb +1 -1
  43. data/lib/misty/openstack/magnum/v1.rb +6 -9
  44. data/lib/misty/openstack/manila/manila_v2.rb +1 -1
  45. data/lib/misty/openstack/manila/v2.rb +6 -9
  46. data/lib/misty/openstack/murano/murano_v1.rb +1 -1
  47. data/lib/misty/openstack/murano/v1.rb +5 -8
  48. data/lib/misty/openstack/neutron/neutron_v2_0.rb +1 -1
  49. data/lib/misty/openstack/neutron/v2_0.rb +5 -8
  50. data/lib/misty/openstack/nova/nova_v2_1.rb +1 -1
  51. data/lib/misty/openstack/nova/v2_1.rb +7 -9
  52. data/lib/misty/openstack/octavia/octavia_v2_0.rb +1 -1
  53. data/lib/misty/openstack/octavia/v2_0.rb +5 -8
  54. data/lib/misty/openstack/sahara/sahara_v1_1.rb +1 -1
  55. data/lib/misty/openstack/sahara/v1_1.rb +5 -8
  56. data/lib/misty/openstack/searchlight/searchlight_v1.rb +1 -1
  57. data/lib/misty/openstack/searchlight/v1.rb +5 -8
  58. data/lib/misty/openstack/senlin/senlin_v1.rb +1 -1
  59. data/lib/misty/openstack/senlin/v1.rb +5 -8
  60. data/lib/misty/openstack/swift/swift_v1.rb +1 -1
  61. data/lib/misty/openstack/swift/v1.rb +17 -9
  62. data/lib/misty/openstack/tacker/tacker_v1_0.rb +1 -1
  63. data/lib/misty/openstack/tacker/v1_0.rb +5 -8
  64. data/lib/misty/openstack/trove/trove_v1_0.rb +1 -1
  65. data/lib/misty/openstack/trove/v1_0.rb +5 -8
  66. data/lib/misty/openstack/zaqar/v2.rb +5 -8
  67. data/lib/misty/openstack/zaqar/zaqar_v2.rb +1 -1
  68. data/lib/misty/version.rb +1 -1
  69. data/test/unit/{http/client_test.rb → client_test.rb} +20 -8
  70. data/test/unit/cloud/requests_test.rb +77 -5
  71. data/test/unit/cloud/services_test.rb +20 -5
  72. data/test/unit/http/header_test.rb +0 -1
  73. data/test/unit/http/method_builder_test.rb +4 -4
  74. data/test/unit/http/request_test.rb +29 -20
  75. data/test/unit/microversion_test.rb +1 -1
  76. data/test/unit/misty_test.rb +18 -0
  77. data/test/unit/openstack/APIs_test.rb +18 -13
  78. data/test/unit/openstack/extension_test.rb +68 -0
  79. data/test/unit/service_helper.rb +1 -1
  80. metadata +8 -4
  81. data/lib/misty/http/client.rb +0 -108
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5dc7dd3f594f1b57498eb00f5ec84950d93f1a7f
4
- data.tar.gz: 55db4993251e461c1a0ff34c71b88b4cbc84d475
3
+ metadata.gz: ffe75b3968e66e94826272e86f37008a8f8588c5
4
+ data.tar.gz: 1d22179b70ec1d3f0defcad2eccd8f375ccd27bc
5
5
  SHA512:
6
- metadata.gz: 25e78f721473b692864310a5adb8a0b59a46a92e704414532647495372711487397f75cedfa0020943624d185dad98985bb25a9be47aeb55e197ebbba1f758ba
7
- data.tar.gz: a387e6ba5f95e5c284a1bf9f1b6259d8703ae2e6d019ed2643bf95a5746d9594d70ee6047964e0b279a12ac1c4a2e8465fa67680f0cef182595044a7764566ad
6
+ metadata.gz: 1a35311aff1b3149a027ecc54843791b350d0c67da4551e0f589c601c5d186721e02754a3b1bcf690964d546c57212f7c9e8cdd30548c142cad7ccd39c5e4274
7
+ data.tar.gz: d5f88faf9a3076df2ddd3f2569b01d9885425d866e2c572069497de6167d782d54af995256b221235eb87c8fdf439060cd02cbdc0d2b053fc0a05cd7d9682040
@@ -1,17 +1,9 @@
1
- require 'misty/http/net_http'
1
+ require 'misty/auth/errors'
2
2
  require 'misty/auth/name'
3
+ require 'misty/http/net_http'
3
4
 
4
5
  module Misty
5
- class Auth
6
- class AuthenticationError < StandardError; end
7
- class CatalogError < StandardError; end
8
- class TokenError < StandardError; end
9
-
10
- class ExpiryError < RuntimeError; end
11
- class CredentialsError < RuntimeError; end
12
- class InitError < RuntimeError; end
13
- class URLError < RuntimeError; end
14
-
6
+ module Auth
15
7
  include Misty::HTTP::NetHTTP
16
8
 
17
9
  attr_reader :catalog, :token
@@ -41,7 +33,7 @@ module Misty
41
33
  Misty::HTTP::NetHTTP.http_request(
42
34
  @uri, ssl_verify_mode: @config.ssl_verify_mode, log: @config.log
43
35
  ) do |connection|
44
- response = connection.post(self.class.path, @credentials.to_json,
36
+ response = connection.post(path, @credentials.to_json,
45
37
  { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
46
38
  unless response.code =~ /200|201/
47
39
  raise AuthenticationError, "Response code=#{response.code}, Msg=#{response.msg}"
@@ -76,7 +68,7 @@ module Misty
76
68
  def find_url(service, region_id, interface)
77
69
  if service['endpoints']
78
70
  service['endpoints'].each do |endpoint|
79
- if (url = self.class.get_url(endpoint, region_id, interface))
71
+ if (url = endpoint_url(endpoint, region_id, interface))
80
72
  return url
81
73
  end
82
74
  end
@@ -1,21 +1,15 @@
1
1
  require 'misty/auth'
2
2
 
3
3
  module Misty
4
- class AuthV2 < Misty::Auth
5
- def self.path
6
- '/v2.0/tokens'
7
- end
8
-
9
- def self.get_url(endpoint, region_id, interface)
10
- return endpoint["#{interface}URL"] if endpoint['region'] == region_id && endpoint["#{interface}URL"]
11
- end
4
+ class AuthV2
5
+ include Misty::Auth
12
6
 
13
7
  def credentials
14
8
  if @token
15
9
  identity = { 'token': { 'id': @token } }
16
10
  else
17
- raise Misty::Auth::CredentialsError, "#{self.class}: User name is required" if @user.name.nil?
18
- raise Misty::Auth::CredentialsError, "#{self.class}: User password is required" if @user.password.nil?
11
+ raise CredentialsError, "#{self.class}: User name is required" if @user.name.nil?
12
+ raise CredentialsError, "#{self.class}: User password is required" if @user.password.nil?
19
13
  identity = { 'passwordCredentials': user_credentials }
20
14
  end
21
15
 
@@ -24,12 +18,20 @@ module Misty
24
18
  elsif @tenant.name
25
19
  identity.merge!('tenantName': @tenant.name)
26
20
  else
27
- raise Misty::Auth::CredentialsError, "#{self.class}: No tenant available"
21
+ raise CredentialsError, "#{self.class}: No tenant available"
28
22
  end
29
23
 
30
24
  { 'auth': identity }
31
25
  end
32
26
 
27
+ def endpoint_url(endpoint, region_id, interface)
28
+ return endpoint["#{interface}URL"] if endpoint['region'] == region_id && endpoint["#{interface}URL"]
29
+ end
30
+
31
+ def path
32
+ '/v2.0/tokens'
33
+ end
34
+
33
35
  def user_credentials
34
36
  {
35
37
  'username': @user.name,
@@ -1,14 +1,8 @@
1
1
  require 'misty/auth'
2
2
 
3
3
  module Misty
4
- class AuthV3 < Misty::Auth
5
- def self.path
6
- '/v3/auth/tokens'
7
- end
8
-
9
- def self.get_url(endpoint, region_id, interface)
10
- return endpoint['url'] if endpoint['region_id'] == region_id && endpoint['interface'] == interface
11
- end
4
+ class AuthV3
5
+ include Misty::Auth
12
6
 
13
7
  def credentials
14
8
  if @token
@@ -30,10 +24,18 @@ module Misty
30
24
  }
31
25
  end
32
26
 
27
+ def endpoint_url(endpoint, region_id, interface)
28
+ return endpoint['url'] if endpoint['region_id'] == region_id && endpoint['interface'] == interface
29
+ end
30
+
31
+ def path
32
+ '/v3/auth/tokens'
33
+ end
34
+
33
35
  def scope
34
36
  return @project.identity if @project
35
37
  return @domain.identity if @domain
36
- raise Misty::Auth::CredentialsError, "#{self.class}: No scope available"
38
+ raise CredentialsError, "#{self.class}: No scope available"
37
39
  end
38
40
 
39
41
  def set(response)
@@ -0,0 +1,12 @@
1
+ module Misty
2
+ module Auth
3
+ class AuthenticationError < StandardError; end
4
+ class CatalogError < StandardError; end
5
+ class TokenError < StandardError; end
6
+
7
+ class ExpiryError < RuntimeError; end
8
+ class CredentialsError < RuntimeError; end
9
+ class InitError < RuntimeError; end
10
+ class URLError < RuntimeError; end
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  module Misty
2
- class Auth
2
+ module Auth
3
3
  module Domain
4
4
  attr_accessor :domain
5
5
 
@@ -11,7 +11,7 @@ module Misty
11
11
  data.merge!(to_h(:name))
12
12
  data.merge!({ :domain => @domain.identity })
13
13
  else
14
- raise Misty::Auth::CredentialsError, "#{self.class}: An Id, or a name with its domain, must be provided"
14
+ raise CredentialsError, "#{self.class}: An Id, or a name with its domain, must be provided"
15
15
  end
16
16
  data
17
17
  end
@@ -21,7 +21,7 @@ module Misty
21
21
  def identity
22
22
  return to_h(:id) unless id.nil?
23
23
  return to_h(:name) unless name.nil?
24
- raise Misty::Auth::CredentialsError, "#{self.class}: No available id or name"
24
+ raise CredentialsError, "#{self.class}: No available id or name"
25
25
  end
26
26
 
27
27
  def to_h(var)
@@ -50,7 +50,7 @@ module Misty
50
50
 
51
51
  def identity
52
52
  data = super
53
- raise Misty::Auth::CredentialsError, "#{self.class}: No password available" if password.nil?
53
+ raise CredentialsError, "#{self.class}: No password available" if password.nil?
54
54
  data.merge!(to_h(:password))
55
55
  { :user => data }
56
56
  end
@@ -0,0 +1,105 @@
1
+ module Misty
2
+ module Client
3
+ class Options
4
+ attr_accessor :base_path, :base_url, :headers, :interface, :region_id,
5
+ :service_names, :ssl_verify_mode, :version
6
+ end
7
+
8
+ class InvalidDataError < StandardError; end
9
+
10
+ INTERFACES = %w{admin public internal}
11
+
12
+ attr_reader :headers, :microversion
13
+
14
+ # Options - Values shown are the default
15
+ # Base path can be forced (Not recommended, mainly used for test purposes)
16
+ # :base_path => nil
17
+ # URL can be forced (Helps when setup is broken)
18
+ # :base_url => nil
19
+ # Optional headers
20
+ # :headers => {}
21
+ # Endpoint type (admin, public or internal)
22
+ # :interface => "public"
23
+ # Region ID
24
+ # :region_id => "regionOne"
25
+ # Service name
26
+ # The Service names are pre defined but more can be added using this option.
27
+ # :service_name
28
+ # SSL Verify Mode
29
+ # :ssl_verify_mode => true
30
+ # (micro)version: Can be numbered (3.1) or by state (CURRENT, LATEST or SUPPORTED)
31
+ # :version => "CURRENT"
32
+ def initialize(auth, config, options = {})
33
+ @auth = auth
34
+ @config = config
35
+ @options = setup(options)
36
+ @uri = URI.parse(@auth.get_url(@options.service_names, @options.region_id, @options.interface))
37
+ @base_path = @options.base_path ? @options.base_path : @uri.path
38
+ @base_path = @base_path.chomp('/')
39
+ @version = nil
40
+ @microversion = false
41
+ @headers = Misty::HTTP::Header.new(@config.headers.get.clone)
42
+ @headers.add('X-Auth-Token' => @auth.get_token.to_s)
43
+ @headers.add(microversion_header) if microversion
44
+ @headers.add(@options.headers) unless @options.headers.empty?
45
+ end
46
+
47
+ # Mixing classes to override
48
+ # When a catalog provides a base path and the Service API definition containts the generic equivalent as prefix
49
+ # then the preifx is redundant and must be removed from the path.
50
+ # For example:
51
+ # Catalog provides 'http://192.0.2.21:8004/v1/48985e6b8da145699d411f12a3459fca'
52
+ # and Service API has '/v1/{tenant_id}/stacks'
53
+ # then the path prefix is ignored and path is only '/stacks'
54
+ def prefix_path_to_ignore
55
+ ''
56
+ end
57
+
58
+ def requests
59
+ requests_api + requests_custom
60
+ end
61
+
62
+ private
63
+
64
+ def baseclass
65
+ self.class.to_s.split('::')[-1]
66
+ end
67
+
68
+ def requests_api
69
+ @requests_api_list ||= begin
70
+ list = []
71
+ api.each do |_path, verbs|
72
+ verbs.each do |_verb, requests|
73
+ list << requests
74
+ end
75
+ end
76
+ list.flatten!
77
+ end
78
+ end
79
+
80
+ def requests_custom
81
+ []
82
+ end
83
+
84
+ def setup(params)
85
+ options = Options.new()
86
+ options.base_path = params[:base_path] ? params[:base_path] : nil
87
+ options.base_url = params[:base_url] ? params[:base_url] : nil
88
+ options.headers = params[:headers] ? params[:headers] : {}
89
+ options.interface = params[:interface] ? params[:interface] : @config.interface
90
+ options.region_id = params[:region_id] ? params[:region_id] : @config.region_id
91
+ options.service_names = params[:service_name] ? service_names << params[:service_name] : service_names
92
+ options.ssl_verify_mode = params[:ssl_verify_mode].nil? ? @config.ssl_verify_mode : params[:ssl_verify_mode]
93
+ options.version = params[:version] ? params[:version] : 'CURRENT'
94
+
95
+ unless INTERFACES.include?(options.interface)
96
+ raise InvalidDataError, "Options ':interface' must be one of #{INTERFACES}"
97
+ end
98
+
99
+ unless options.ssl_verify_mode == !!options.ssl_verify_mode
100
+ raise InvalidDataError, "Options ':ssl_verify_mode' must be a boolean"
101
+ end
102
+ options
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,16 @@
1
+ require 'misty/client'
2
+ require 'misty/http/net_http'
3
+ require 'misty/http/method_builder'
4
+ require 'misty/http/request'
5
+ require 'misty/http/direct'
6
+ require 'misty/http/header'
7
+
8
+ module Misty
9
+ module ClientPack
10
+ include Misty::Client
11
+ include Misty::HTTP::NetHTTP
12
+ include Misty::HTTP::MethodBuilder
13
+ include Misty::HTTP::Request
14
+ include Misty::HTTP::Direct
15
+ end
16
+ end
@@ -11,9 +11,22 @@ module Misty
11
11
 
12
12
  private
13
13
 
14
+ def process_data(header, args)
15
+ if args.size == 1 && Misty.json_encode?(args[0])
16
+ header.add('Content-Type' => 'application/json')
17
+ return Misty.to_json(args[0])
18
+ elsif args.size == 1
19
+ return args[0]
20
+ end
21
+ end
22
+
14
23
  def method_call(method, *args)
15
- header = @headers.get.clone
16
- header.merge!(args.pop.get) if args[-1].class == Misty::HTTP::Header
24
+ if args[-1].class == Misty::HTTP::Header
25
+ header = Misty::HTTP::Header.new(@headers.get.clone)
26
+ header.add(args.pop.get)
27
+ else
28
+ header = @headers
29
+ end
17
30
 
18
31
  base, path = prefixed_path(method[:path])
19
32
  size_path_parameters = count_path_params(path)
@@ -24,23 +37,26 @@ module Misty
24
37
 
25
38
  case method[:request]
26
39
  when :COPY
27
- http_copy(final_path, header)
40
+ http_copy(final_path, header.get)
28
41
  when :DELETE
29
- http_delete(final_path, header)
42
+ http_delete(final_path, header.get)
30
43
  when :GET
31
- final_path << query_param(args_left[0]) if args_left && args_left.size == 1
32
- http_get(final_path, header)
44
+ final_path << query_param(args_left.shift) if args_left && args_left.size == 1
45
+ http_get(final_path, header.get)
33
46
  when :HEAD
34
- http_head(final_path, header)
47
+ http_head(final_path, header.get)
35
48
  when :POST
36
- data = args_left[0] if args_left && args_left.size == 1
37
- http_post(final_path, header, data)
49
+ final_path << query_param(args_left.shift) if args_left && args_left.size == 2
50
+ data = process_data(header, args_left)
51
+ http_post(final_path, header.get, data)
38
52
  when :PUT
39
- data = args_left[0] if args_left && args_left.size == 1
40
- http_put(final_path, header, data)
53
+ final_path << query_param(args_left.shift) if args_left && args_left.size == 2
54
+ data = process_data(header, args_left)
55
+ http_put(final_path, header.get, data)
41
56
  when :PATCH
42
- data = args_left[0] if args_left && args_left.size == 1
43
- http_patch(final_path, header, data)
57
+ final_path << query_param(args_left.shift) if args_left && args_left.size == 2
58
+ data = process_data(header, args_left)
59
+ http_patch(final_path, header.get, data)
44
60
  else
45
61
  raise SyntaxError, "Invalid HTTP request: #{method[:request]}"
46
62
  end
@@ -58,10 +74,10 @@ module Misty
58
74
  end
59
75
 
60
76
  def get_method(name)
61
- self.class.api.each do |path, requests|
62
- requests.each do |request, methods_list|
63
- if methods_list.include?(name)
64
- return {:path => path, :request => request, :name => name}
77
+ api.each do |path, verbs_list|
78
+ verbs_list.each do |verb, methods|
79
+ if methods.include?(name)
80
+ return {:path => path, :request => verb, :name => name}
65
81
  end
66
82
  end
67
83
  end
@@ -77,9 +93,9 @@ module Misty
77
93
  end
78
94
 
79
95
  def prefixed_path(path)
80
- unless self.class.prefix_path_to_ignore.empty?
81
- if path =~ %r{#{self.class.prefix_path_to_ignore}}
82
- return true, path.gsub(self.class.prefix_path_to_ignore, '')
96
+ unless prefix_path_to_ignore.empty?
97
+ if path =~ %r{#{prefix_path_to_ignore}}
98
+ return true, path.gsub(prefix_path_to_ignore, '')
83
99
  else
84
100
  return false, path
85
101
  end
@@ -48,6 +48,20 @@ module Misty
48
48
  # Default when uri.scheme is https
49
49
  SSL_VERIFY_MODE = true
50
50
 
51
+ def self.json_encode?(data)
52
+ return true if data.is_a?(Array) || data.is_a?(Hash)
53
+ if data.is_a?(String)
54
+ begin
55
+ JSON.parse(data)
56
+ rescue JSON::ParserError
57
+ return false
58
+ else
59
+ return true
60
+ end
61
+ end
62
+ return false
63
+ end
64
+
51
65
  def self.services
52
66
  services = Misty::Services.new
53
67
  SERVICES.each do |service|
@@ -1,5 +1,5 @@
1
1
  module Misty::Openstack::AodhV2
2
- def v2
2
+ def api
3
3
  {"/v2/capabilities"=>{:GET=>[:list_capabilities]},
4
4
  "/v2/alarms"=>{:GET=>[:list_alarms], :POST=>[:create_alarm]},
5
5
  "/v2/alarms/{alarm_id}"=>
@@ -1,17 +1,14 @@
1
- require 'misty/http/client'
2
1
  require 'misty/openstack/aodh/aodh_v2'
2
+ require 'misty/client_pack'
3
3
 
4
4
  module Misty
5
5
  module Openstack
6
6
  module Aodh
7
- class V2 < Misty::HTTP::Client
8
- extend Misty::Openstack::AodhV2
7
+ class V2
8
+ include Misty::Openstack::AodhV2
9
+ include Misty::ClientPack
9
10
 
10
- def self.api
11
- v2
12
- end
13
-
14
- def self.service_names
11
+ def service_names
15
12
  %w{alarming}
16
13
  end
17
14
  end