misty 1.3.3 → 1.4.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +332 -267
  3. data/lib/misty.rb +1 -1
  4. data/lib/misty/auth.rb +17 -6
  5. data/lib/misty/auth/auth_v2.rb +3 -0
  6. data/lib/misty/auth/auth_v3.rb +13 -5
  7. data/lib/misty/auth/name.rb +3 -3
  8. data/lib/misty/client_pack.rb +2 -2
  9. data/lib/misty/cloud.rb +111 -76
  10. data/lib/misty/config.rb +138 -0
  11. data/lib/misty/{auth/errors.rb → errors.rb} +9 -1
  12. data/lib/misty/http/direct.rb +18 -1
  13. data/lib/misty/http/method_builder.rb +10 -17
  14. data/lib/misty/http/net_http.rb +1 -1
  15. data/lib/misty/http/request.rb +26 -14
  16. data/lib/misty/microversion.rb +22 -41
  17. data/lib/misty/misty.rb +14 -24
  18. data/lib/misty/openstack/cinder/v3.rb +8 -0
  19. data/lib/misty/openstack/ironic/v1.rb +8 -0
  20. data/lib/misty/openstack/magnum/v1.rb +5 -1
  21. data/lib/misty/openstack/manila/v2.rb +8 -0
  22. data/lib/misty/openstack/nova/v2_1.rb +13 -8
  23. data/lib/misty/openstack/service.rb +88 -0
  24. data/lib/misty/openstack/swift/v1.rb +2 -2
  25. data/lib/misty/service.rb +9 -12
  26. data/lib/misty/version.rb +1 -1
  27. data/test/integration/{network_test.rb → networking_test.rb} +8 -8
  28. data/test/integration/test_helper.rb +1 -0
  29. data/test/integration/vcr/{network_using_neutron_v2_0.yml → networking_using_neutron_v2_0.yml} +0 -0
  30. data/test/unit/auth/name_test.rb +31 -27
  31. data/test/unit/auth_helper.rb +4 -4
  32. data/test/unit/auth_test.rb +44 -30
  33. data/test/unit/cloud/config_test.rb +165 -0
  34. data/test/unit/cloud/requests_test.rb +0 -12
  35. data/test/unit/cloud/services_test.rb +41 -12
  36. data/test/unit/cloud_test.rb +35 -44
  37. data/test/unit/http/request_test.rb +1 -1
  38. data/test/unit/microversion_test.rb +59 -35
  39. data/test/unit/misty_test.rb +1 -1
  40. data/test/unit/openstack/service_test.rb +52 -0
  41. data/test/unit/service_helper.rb +23 -20
  42. data/test/unit/services_test.rb +1 -1
  43. data/test/unit/test_helper.rb +0 -4
  44. metadata +37 -22
  45. data/lib/misty/client.rb +0 -104
  46. data/test/unit/client_test.rb +0 -97
@@ -1,4 +1,3 @@
1
- require 'logger'
2
1
  require 'json'
3
2
  require 'net/http'
4
3
  require 'time'
@@ -6,3 +5,4 @@ require 'uri'
6
5
  require 'misty/misty'
7
6
  require 'misty/autoload'
8
7
  require 'misty/cloud'
8
+ require 'misty/errors'
@@ -1,20 +1,29 @@
1
- require 'misty/auth/errors'
1
+ require 'logger'
2
2
  require 'misty/auth/name'
3
3
  require 'misty/http/net_http'
4
+ require 'misty/config'
4
5
 
5
6
  module Misty
7
+
8
+ # The credentials are a combination of "id" and "name" used to uniquely identify the context.
9
+ # +Misty::Auth+ is mixing the common interface between +Misty::AuthV3+ and +Misty::AuthV2+
10
+ #
6
11
  module Auth
7
12
  include Misty::HTTP::NetHTTP
8
13
 
9
14
  attr_reader :catalog, :token
10
15
 
11
- def self.factory(auth, config)
16
+ def self.build(auth)
12
17
  version = auth[:tenant_id] || auth[:tenant] ? 'V2' : 'V3'
13
18
  klass = Object.const_get("Misty::Auth#{version}")
14
- klass.new(auth, config)
19
+ klass.new(auth)
15
20
  end
16
21
 
17
- def initialize(auth, config)
22
+ # ==== Attributes
23
+ #
24
+ # * +auth+ - Hash of credentials parameters for authentication
25
+ #
26
+ def initialize(auth)
18
27
  if auth[:context]
19
28
  # bypass the authentication by given token catalog and expire date
20
29
  @token = auth[:context][:token]
@@ -23,15 +32,17 @@ module Misty
23
32
  else
24
33
  raise URLError, 'No URL provided' if auth[:url].nil? || auth[:url].empty?
25
34
  @uri = URI.parse(auth[:url])
26
- @config = config
35
+ @ssl_verify_mode = auth[:ssl_verify_mode] ? auth[:ssl_verify_mode] : Misty::Config::SSL_VERIFY_MODE
27
36
  @credentials = set_credentials(auth)
28
37
  @token, @catalog, @expires = set(authenticate)
38
+ # TODO: Pass main log object
39
+ @log = Logger.new('/dev/null')
29
40
  end
30
41
  end
31
42
 
32
43
  def authenticate
33
44
  Misty::HTTP::NetHTTP.http_request(
34
- @uri, ssl_verify_mode: @config.ssl_verify_mode, log: @config.log
45
+ @uri, ssl_verify_mode: @ssl_verify_mode, log: @log
35
46
  ) do |connection|
36
47
  response = connection.post(path, @credentials.to_json,
37
48
  { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
@@ -1,6 +1,9 @@
1
1
  require 'misty/auth'
2
2
 
3
3
  module Misty
4
+ #
5
+ # Openstack Identity service Keystone version 2.0, which is deprecated, uses a tenant name or id to authenticates
6
+ # +Misty::AuthV2+ is used if the authentication credentials contains a tenant name or id.
4
7
  class AuthV2
5
8
  include Misty::Auth
6
9
 
@@ -48,10 +48,11 @@ module Misty
48
48
 
49
49
  def set_credentials(auth)
50
50
  if auth[:project_id] || auth[:project]
51
- # scope: project
52
- project_domain_id = auth[:project_domain_id]
53
- if project_domain_id.nil? && auth[:project_domain].nil?
51
+ if auth[:project_domain_id].nil? && auth[:project_domain].nil?
54
52
  project_domain_id = Misty::DOMAIN_ID
53
+ else
54
+ project_domain_id = auth[:project_domain_id] if auth[:project_domain_id]
55
+ project_domain = auth[:project_domain] if auth[:project_domain]
55
56
  end
56
57
 
57
58
  @project = Misty::Auth::ProjectScope.new(auth[:project_id], auth[:project])
@@ -69,10 +70,17 @@ module Misty
69
70
  if auth[:token]
70
71
  @token = auth[:token]
71
72
  else
72
- user_domain_id = auth[:user_domain_id] ? auth[:user_domain_id] : Misty::DOMAIN_ID
73
73
  @user = Misty::Auth::User.new(auth[:user_id], auth[:user])
74
+
75
+ if auth[:user_domain_id].nil? && auth[:user_domain].nil?
76
+ user_domain_id = Misty::DOMAIN_ID
77
+ else
78
+ user_domain_id = auth[:user_domain_id] if auth[:user_domain_id]
79
+ user_domain = auth[:user_domain] if auth[:user_domain]
80
+ end
81
+
82
+ @user.domain = Misty::Auth::Name.new(user_domain_id, user_domain)
74
83
  @user.password = auth[:password]
75
- @user.domain = Misty::Auth::Name.new(user_domain_id, auth[:user_domain])
76
84
  end
77
85
 
78
86
  credentials
@@ -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 CredentialsError, "#{self.class}: An Id, or a name with its domain, must be provided"
14
+ raise Misty::Config::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 CredentialsError, "#{self.class}: No available id or name"
24
+ raise Misty::Config::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 CredentialsError, "#{self.class}: No password available" if password.nil?
53
+ raise Misty::Config::CredentialsError, "#{self.class}: No password available" if password.nil?
54
54
  data.merge!(to_h(:password))
55
55
  { :user => data }
56
56
  end
@@ -1,4 +1,4 @@
1
- require 'misty/client'
1
+ require 'misty/openstack/service'
2
2
  require 'misty/http/net_http'
3
3
  require 'misty/http/method_builder'
4
4
  require 'misty/http/request'
@@ -7,7 +7,7 @@ require 'misty/http/header'
7
7
 
8
8
  module Misty
9
9
  module ClientPack
10
- include Misty::Client
10
+ include Misty::Openstack::Service
11
11
  include Misty::HTTP::NetHTTP
12
12
  include Misty::HTTP::MethodBuilder
13
13
  include Misty::HTTP::Request
@@ -1,150 +1,185 @@
1
+ require 'misty/config'
1
2
  require 'misty/auth/auth_v2'
2
3
  require 'misty/auth/auth_v3'
3
4
  require 'misty/http/header'
4
5
 
5
6
  module Misty
6
- class Cloud
7
- class Config
8
- attr_accessor :auth, :content_type, :interface, :log, :region_id, :ssl_verify_mode, :headers
9
- end
10
-
11
- attr_reader :auth
12
7
 
8
+ # +Misty::Cloud+ is the main OpenStack cloud class.
9
+ # An instance holds authentication information such as token, catalog and contains all available services as methods.
10
+ #
11
+ class Cloud
13
12
  def self.dot_to_underscore(val)
14
13
  val.gsub(/\./,'_')
15
14
  end
16
15
 
17
- def initialize(params)
18
- @params = params
19
- @config = self.class.set_configuration(params)
20
- @auth = Misty::Auth.factory(params[:auth], @config)
21
- end
22
-
23
- def self.set_configuration(params)
24
- config = Config.new
25
- config.content_type = params[:content_type] ? params[:content_type] : Misty::CONTENT_TYPE
26
- config.interface = params[:interface] ? params[:interface] : Misty::INTERFACE
27
- config.log = Logger.new(params[:log_file] ? params[:log_file] : Misty::LOG_FILE)
28
- config.log.level = params[:log_level] ? params[:log_level] : Misty::LOG_LEVEL
29
- config.region_id = params[:region_id] ? params[:region_id] : Misty::REGION_ID
30
- config.ssl_verify_mode = params.key?(:ssl_verify_mode) ? params[:ssl_verify_mode] : Misty::SSL_VERIFY_MODE
31
- config.headers = Misty::HTTP::Header.new('Accept' => 'application/json; q=1.0')
32
- config.headers.add(params[:headers]) if params[:headers] && !params[:headers].empty?
33
- config
16
+ # ==== Attributes
17
+ #
18
+ # * +arg+ - Hash of configuration parameters for authentication, log, and services options.
19
+ #
20
+ def initialize(arg)
21
+ @config = Misty::Config.new(arg)
34
22
  end
35
23
 
36
- def build_service(service_name)
37
- service = Misty.services.find {|service| service.name == service_name}
38
- options = @params[service.name] ? @params[service.name] : {}
39
- version = self.class.dot_to_underscore(service.version(options[:api_version]))
24
+ def build_service(method)
25
+ service = Misty.services.find {|service| service.name == method}
26
+ service_config = @config.get_service(method)
27
+ version = self.class.dot_to_underscore(service.default_version(service_config[:config][:api_version]))
40
28
  klass = Object.const_get("Misty::Openstack::#{service.project.capitalize}::#{version.capitalize}")
41
- klass.new(@auth, @config, options)
29
+ klass.new(service_config)
42
30
  end
43
31
 
44
- def application_catalog
45
- @application_catalog ||= build_service(:application_catalog)
32
+ def application_catalog(arg = {})
33
+ @application_catalog ||= build_service(__method__)
34
+ @application_catalog.request_config(arg)
35
+ @application_catalog
46
36
  end
47
37
 
48
- def alarming
49
- @alarming ||= build_service(:alarming)
38
+ def alarming(arg = {})
39
+ @alarming ||= build_service(__method__)
40
+ @alarming.request_config(arg)
41
+ @alarming
50
42
  end
51
43
 
52
- def backup
53
- @backup ||= build_service(:backup)
44
+ def backup(arg = {})
45
+ @backup ||= build_service(__method__)
46
+ @backup.request_config(arg)
47
+ @backup
54
48
  end
55
49
 
56
- def baremetal
57
- @baremetal ||= build_service(:baremetal)
50
+ def baremetal(arg = {})
51
+ @baremetal ||= build_service(__method__)
52
+ @baremetal.request_config(arg)
53
+ @baremetal
58
54
  end
59
55
 
60
- def block_storage
61
- @block_storage ||= build_service(:block_storage)
56
+ def block_storage(arg = {})
57
+ @block_storage ||= build_service(__method__)
58
+ @block_storage.request_config(arg)
59
+ @block_storage
62
60
  end
63
61
 
64
- def clustering
65
- @clustering ||= build_service(:clustering)
62
+ def clustering(arg = {})
63
+ @clustering ||= build_service(__method__)
64
+ @clustering.request_config(arg)
65
+ @clustering
66
66
  end
67
67
 
68
- def compute
69
- @compute ||= build_service(:compute)
68
+ def compute(arg = {})
69
+ @compute ||= build_service(__method__)
70
+ @compute.request_config(arg)
71
+ @compute
70
72
  end
71
73
 
72
- def container_infrastructure_management
73
- @container_infrastructure_management ||= build_service(:container_infrastructure_management)
74
+ def container_infrastructure_management(arg = {})
75
+ @container_infrastructure_management ||= build_service(__method__)
76
+ @container_infrastructure_management.request_config(arg)
77
+ @container_infrastructure_management
74
78
  end
75
79
 
76
- def data_processing
77
- @data_processing ||= build_service(:data_processing)
80
+ def data_processing(arg = {})
81
+ @data_processing ||= build_service(__method__)
82
+ @data_processing.request_config(arg)
83
+ @data_processing
78
84
  end
79
85
 
80
- def data_protection_orchestration
81
- @data_protection_orchestration ||= build_service(:data_protection_orchestration)
86
+ def data_protection_orchestration(arg = {})
87
+ @data_protection_orchestration ||= build_service(__method__)
88
+ @data_protection_orchestration.request_config(arg)
89
+ @data_protection_orchestration
82
90
  end
83
91
 
84
- def database
85
- @database ||= build_service(:database)
92
+ def database(arg = {})
93
+ @database ||= build_service(__method__)
94
+ @database.request_config(arg)
95
+ @database
86
96
  end
87
97
 
88
- def domain_name_server
89
- @domain_name_server ||= build_service(:domain_name_server)
98
+ def dns(arg = {})
99
+ @dns ||= build_service(__method__)
100
+ @dns.request_config(arg)
101
+ @dns
90
102
  end
91
103
 
92
- def identity
93
- @identity ||= build_service(:identity)
104
+ def identity(arg = {})
105
+ @identity ||= build_service(__method__)
106
+ @identity.request_config(arg)
107
+ @identity
94
108
  end
95
109
 
96
- def image
97
- @image ||= build_service(:image)
110
+ def image(arg = {})
111
+ @image ||= build_service(__method__)
112
+ @image.request_config(arg)
113
+ @image
98
114
  end
99
115
 
100
- def load_balancer
101
- @load_balancer ||= build_service(:load_balancer)
116
+ def load_balancer(arg = {})
117
+ @load_balancer ||= build_service(__method__)
118
+ @load_balancer.request_config(arg)
119
+ @load_balancer
102
120
  end
103
121
 
104
- def messaging
105
- @messaging ||= build_service(:messaging)
122
+ def messaging(arg = {})
123
+ @messaging ||= build_service(__method__)
124
+ @messaging.request_config(arg)
125
+ @messaging
106
126
  end
107
127
 
108
- def metering
109
- @metering ||= build_service(:metering)
128
+ def metering(arg = {})
129
+ @metering ||= build_service(__method__)
130
+ @metering.request_config(arg)
131
+ @metering
110
132
  end
111
133
 
112
- def networking
113
- @networking ||= build_service(:networking)
134
+ def networking(arg = {})
135
+ @networking ||= build_service(__method__)
136
+ @networking.request_config(arg)
137
+ @networking
114
138
  end
115
139
 
116
- def nfv_orchestration
117
- @nfv_orchestration ||= build_service(:nfv_orchestration)
140
+ def nfv_orchestration(arg = {})
141
+ @nfv_orchestration ||= build_service(__method__)
142
+ @nfv_orchestration.request_config(arg)
143
+ @nfv_orchestration
118
144
  end
119
145
 
120
- def object_storage
121
- @object_storage ||= build_service(:object_storage)
146
+ def object_storage(arg = {})
147
+ @object_storage ||= build_service(__method__)
148
+ @object_storage.request_config(arg)
149
+ @object_storage
122
150
  end
123
151
 
124
- def orchestration
125
- @orchestration ||= build_service(:orchestration)
152
+ def orchestration(arg = {})
153
+ @orchestration ||= build_service(__method__)
154
+ @orchestration.request_config(arg)
155
+ @orchestration
126
156
  end
127
157
 
128
- def search
129
- @search ||= build_service(:search)
158
+ def search(arg = {})
159
+ @search ||= build_service(__method__)
160
+ @search.request_config(arg)
161
+ @search
130
162
  end
131
163
 
132
- def shared_file_systems
133
- @shared_file_systems ||= build_service(:shared_file_systems)
164
+ def shared_file_systems(arg = {})
165
+ @shared_file_systems ||= build_service(__method__)
166
+ @shared_file_systems.request_config(arg)
167
+ @shared_file_systems
134
168
  end
135
169
 
136
- alias dns domain_name_server
170
+ alias domain_name_server dns
137
171
  alias volume block_storage
138
172
 
139
173
  private
140
174
 
141
- def method_missing(method_name)
175
+ def method_missing(method_name) # TODO, *args)
142
176
  services_avail = []
143
177
  Misty.services.names.each do |service_name|
144
178
  services_avail << service_name if /#{method_name}/.match(service_name)
145
179
  end
146
180
 
147
181
  if services_avail.size == 1
182
+ # TODO: Add args
148
183
  self.send(services_avail[0])
149
184
  return self.instance_variable_get("@#{services_avail[0]}")
150
185
  elsif services_avail.size > 1
@@ -0,0 +1,138 @@
1
+ require 'logger'
2
+ require 'misty/auth/auth_v2'
3
+ require 'misty/auth/auth_v3'
4
+ require 'misty/http/header'
5
+
6
+ module Misty
7
+ class Config
8
+ # Default REST content type. Use :json or :hash
9
+ CONTENT_TYPE = :hash
10
+
11
+ # Valid content format
12
+ CONTENT_TYPES = %i{hash json}
13
+
14
+ # Default Interface
15
+ INTERFACE = 'public'
16
+
17
+ # Valid endpoint interfaces
18
+ INTERFACES = %w{admin public internal}
19
+
20
+ # Default Log file
21
+ LOG_FILE = '/dev/null'
22
+
23
+ # Default Log level
24
+ LOG_LEVEL = Logger::INFO
25
+
26
+ # Default Region
27
+ REGION_ID = 'regionOne'
28
+
29
+ # Default when uri.scheme is https
30
+ SSL_VERIFY_MODE = true
31
+
32
+ # ==== Attributes
33
+ #
34
+ # * +arg+ - +Hash+ of configuration options
35
+
36
+ attr_reader :auth, :log, :services
37
+
38
+ def initialize(arg)
39
+ raise CredentialsError if arg.nil? || arg.empty? || arg[:auth].nil? || arg[:auth].empty?
40
+ @auth = Misty::Auth.build(arg[:auth]) # TODO: pass @log
41
+ @log = set_log(arg[:log_file], arg[:log_level])
42
+ @globals = set_config(arg)
43
+ @services = {}
44
+ # TODO: Adjust Services to use enumerable
45
+ arg.each do |e, k|
46
+ Misty::SERVICES.each do |serv|
47
+ @services[e] = k if serv[:name] == e
48
+ end
49
+ end
50
+ end
51
+
52
+ def get_service(method)
53
+ set = {}
54
+ set[:auth] = @auth
55
+ set[:log] = @log
56
+ service_config = @services.key?(method) ? @services[method] : {}
57
+ if service_config
58
+ set[:config] = set_config(service_config, @globals)
59
+ set[:config].merge!(set_service(service_config))
60
+ else
61
+ set[:config] = @globals
62
+ end
63
+ set
64
+ end
65
+
66
+ def set_service(arg)
67
+ set = {}
68
+ set[:base_path] = arg[:base_path] ? arg[:base_path] : nil
69
+ set[:base_url] = arg[:base_url] ? arg[:base_url] : nil
70
+ set[:version] = arg[:version] ? arg[:version] : nil
71
+ set[:api_version] = arg[:api_version] ? arg[:api_version] : nil
72
+ set
73
+ end
74
+
75
+ private
76
+
77
+ def get_defaults
78
+ set = {}
79
+ set[:content_type] = CONTENT_TYPE
80
+ set[:headers] = HTTP::Header.new('Accept' => 'application/json; q=1.0')
81
+ set[:interface] = INTERFACE
82
+ set[:region_id] = REGION_ID
83
+ set[:ssl_verify_mode] = SSL_VERIFY_MODE
84
+ set
85
+ end
86
+
87
+ def set_config(arg = {}, defaults = get_defaults)
88
+ set = {}
89
+ set[:content_type] = set_content_type(arg[:content_type], defaults[:content_type])
90
+ set[:headers] = set_headers(arg[:headers], defaults[:headers])
91
+ set[:interface] = set_interface(arg[:interface], defaults[:interface])
92
+ set[:region_id] = set_region_id(arg[:region_id], defaults[:region_id])
93
+ set[:ssl_verify_mode] = set_ssl_verify_mode(arg[:ssl_verify_mode], defaults[:ssl_verify_mode])
94
+ set
95
+ end
96
+
97
+
98
+ def set_content_type(val, default)
99
+ res = val.nil? ? default : val
100
+ raise InvalidDataError, "Config ':content_type' must be one of #{CONTENT_TYPES}" unless CONTENT_TYPES.include?(res)
101
+ res
102
+ end
103
+
104
+ def set_headers(val, default)
105
+ res = if val && !val.empty?
106
+ default.add(val)
107
+ default
108
+ else default
109
+ default
110
+ end
111
+ res
112
+ end
113
+
114
+ def set_interface(val, default)
115
+ res = val.nil? ? default : val
116
+ raise InvalidDataError, "Config ':interface' must be one of #{INTERFACES}" unless INTERFACES.include?(res)
117
+ res
118
+ end
119
+
120
+ def set_log(file, level)
121
+ log = Logger.new(file ? file : LOG_FILE)
122
+ log.level = level ? level : LOG_LEVEL
123
+ log
124
+ end
125
+
126
+ def set_region_id(val, default)
127
+ res = val.nil? ? default : val
128
+ raise InvalidDataError, "Config ':region_id' must be a String" unless res.kind_of? String
129
+ res
130
+ end
131
+
132
+ def set_ssl_verify_mode(val, default)
133
+ res = val.nil? ? default : val
134
+ raise InvalidDataError, "Config ':ssl_verify_mode' must be a Boolean" unless res == !!res
135
+ res
136
+ end
137
+ end
138
+ end