miasma 0.1.0 → 0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/LICENSE +13 -0
- data/README.md +18 -3
- data/lib/miasma/contrib/aws.rb +18 -1
- data/lib/miasma/contrib/aws/orchestration.rb +3 -3
- data/lib/miasma/contrib/aws/storage.rb +330 -0
- data/lib/miasma/contrib/open_stack.rb +334 -0
- data/lib/miasma/contrib/open_stack/compute.rb +105 -0
- data/lib/miasma/contrib/open_stack/orchestration.rb +255 -0
- data/lib/miasma/contrib/rackspace.rb +34 -94
- data/lib/miasma/contrib/rackspace/compute.rb +1 -92
- data/lib/miasma/contrib/rackspace/orchestration.rb +1 -231
- data/lib/miasma/error.rb +7 -5
- data/lib/miasma/models/orchestration/resource.rb +1 -1
- data/lib/miasma/models/orchestration/stack.rb +2 -2
- data/lib/miasma/models/storage.rb +36 -8
- data/lib/miasma/models/storage/bucket.rb +23 -10
- data/lib/miasma/models/storage/file.rb +44 -4
- data/lib/miasma/models/storage/files.rb +6 -1
- data/lib/miasma/types/api.rb +10 -4
- data/lib/miasma/types/model.rb +4 -1
- data/lib/miasma/utils/lazy.rb +7 -2
- data/lib/miasma/version.rb +1 -1
- data/miasma.gemspec +2 -1
- metadata +21 -2
@@ -1,19 +1,38 @@
|
|
1
1
|
require 'miasma'
|
2
|
-
require 'miasma/
|
3
|
-
require 'time'
|
2
|
+
require 'miasma/contrib/open_stack'
|
4
3
|
|
5
4
|
module Miasma
|
6
5
|
module Contrib
|
7
6
|
|
8
7
|
# Rackspace API core helper
|
9
|
-
class RackspaceApiCore
|
8
|
+
class RackspaceApiCore < OpenStackApiCore
|
9
|
+
|
10
|
+
# Authentication helper class
|
11
|
+
class Authenticate < OpenStackApiCore::Authenticate
|
12
|
+
# Authentication implementation compatible for v2
|
13
|
+
class Version2 < OpenStackApiCore::Authenticate::Version2
|
14
|
+
|
15
|
+
# @return [Smash] authentication request body
|
16
|
+
def authentication_request
|
17
|
+
Smash.new(
|
18
|
+
'RAX-KSKEY:apiKeyCredentials' => Smash.new(
|
19
|
+
'username' => credentials[:open_stack_username],
|
20
|
+
'apiKey' => credentials[:open_stack_token]
|
21
|
+
)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
10
27
|
|
28
|
+
# Common API methods
|
11
29
|
module ApiCommon
|
12
30
|
|
13
31
|
# Set attributes into model
|
14
32
|
#
|
15
33
|
# @param klass [Class]
|
16
34
|
def self.included(klass)
|
35
|
+
klass.attributes.clear
|
17
36
|
klass.class_eval do
|
18
37
|
attribute :rackspace_api_key, String, :required => true
|
19
38
|
attribute :rackspace_username, String, :required => true
|
@@ -21,26 +40,8 @@ module Miasma
|
|
21
40
|
end
|
22
41
|
end
|
23
42
|
|
24
|
-
# @return [HTTP] with auth token provided
|
25
|
-
def connection
|
26
|
-
super.with_headers('X-Auth-Token' => token)
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [String] endpoint URL
|
30
|
-
def endpoint
|
31
|
-
rackspace_api.endpoint_for(
|
32
|
-
Utils.snake(self.class.to_s.split('::')[-2]).to_sym,
|
33
|
-
rackspace_region
|
34
|
-
)
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [String] valid API token
|
38
|
-
def token
|
39
|
-
rackspace_api.api_token
|
40
|
-
end
|
41
|
-
|
42
43
|
# @return [Miasma::Contrib::RackspaceApiCore]
|
43
|
-
def
|
44
|
+
def open_stack_api
|
44
45
|
key = "miasma_rackspace_api_#{attributes.checksum}".to_sym
|
45
46
|
memoize(key, :direct) do
|
46
47
|
Miasma::Contrib::RackspaceApiCore.new(attributes)
|
@@ -70,88 +71,27 @@ module Miasma
|
|
70
71
|
'load_balancer' => 'cloudLoadBalancers'
|
71
72
|
)
|
72
73
|
|
73
|
-
# @return [String] username
|
74
|
-
attr_reader :user
|
75
|
-
# @return [Smash] remote service catalog
|
76
|
-
attr_reader :service_catalog
|
77
|
-
# @return [Smash] token information
|
78
|
-
attr_reader :token
|
79
|
-
# @return [Smash] credentials in use
|
80
|
-
attr_reader :credentials
|
81
|
-
|
82
74
|
# Create a new api instance
|
83
75
|
#
|
84
76
|
# @param creds [Smash] credential hash
|
85
77
|
# @return [self]
|
86
78
|
def initialize(creds)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
#
|
92
|
-
# @param api_name [String] name of api
|
93
|
-
# @param region [String] region in use
|
94
|
-
# @return [String] public URL
|
95
|
-
def endpoint_for(api_name, region)
|
96
|
-
identify_and_load unless service_catalog
|
97
|
-
api = API_MAP[api_name]
|
98
|
-
srv = service_catalog.detect do |info|
|
99
|
-
info[:name] == api
|
79
|
+
if(creds[:rackspace_region].to_s == 'lon')
|
80
|
+
endpoint = AUTH_ENDPOINT[:uk]
|
81
|
+
else
|
82
|
+
endpoint = AUTH_ENDPOINT[:us]
|
100
83
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
if(point)
|
109
|
-
point[:publicURL]
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# @return [String] API token
|
114
|
-
def api_token
|
115
|
-
if(token.nil? || Time.now > token[:expires])
|
116
|
-
identify_and_load
|
117
|
-
end
|
118
|
-
token[:id]
|
84
|
+
super Smash.new(
|
85
|
+
:open_stack_username => creds[:rackspace_username],
|
86
|
+
:open_stack_token => creds[:rackspace_api_key],
|
87
|
+
:open_stack_region => creds[:rackspace_region],
|
88
|
+
:open_stack_identity_url => endpoint
|
89
|
+
)
|
119
90
|
end
|
120
91
|
|
121
92
|
# @return [String] ID of account
|
122
93
|
def account_id
|
123
|
-
|
124
|
-
identify_and_load
|
125
|
-
end
|
126
|
-
token[:tenant][:id]
|
127
|
-
end
|
128
|
-
|
129
|
-
# Identify with authentication service and load
|
130
|
-
# token information and service catalog
|
131
|
-
#
|
132
|
-
# @return [TrueClass]
|
133
|
-
def identify_and_load
|
134
|
-
endpoint = credentials[:rackspace_region].to_s == 'lon' ? AUTH_ENDPOINT[:uk] : AUTH_ENDPOINT[:us]
|
135
|
-
result = HTTP.post(File.join(endpoint, 'tokens'),
|
136
|
-
:json => {
|
137
|
-
'auth' => {
|
138
|
-
'RAX-KSKEY:apiKeyCredentials' => {
|
139
|
-
'username' => credentials[:rackspace_username],
|
140
|
-
'apiKey' => credentials[:rackspace_api_key]
|
141
|
-
}
|
142
|
-
}
|
143
|
-
}
|
144
|
-
)
|
145
|
-
unless(result.status == 200)
|
146
|
-
raise Error::ApiError::AuthenticationError.new('Failed to authenticate', :response => result)
|
147
|
-
end
|
148
|
-
info = MultiJson.load(result.body.to_s).to_smash
|
149
|
-
info = info[:access]
|
150
|
-
@user = info[:user]
|
151
|
-
@service_catalog = info[:serviceCatalog]
|
152
|
-
@token = info[:token]
|
153
|
-
token[:expires] = Time.parse(token[:expires])
|
154
|
-
true
|
94
|
+
identity.token[:tenant][:id]
|
155
95
|
end
|
156
96
|
|
157
97
|
end
|
@@ -3,101 +3,10 @@ require 'miasma'
|
|
3
3
|
module Miasma
|
4
4
|
module Models
|
5
5
|
class Compute
|
6
|
-
class Rackspace <
|
6
|
+
class Rackspace < OpenStack
|
7
7
|
|
8
8
|
include Contrib::RackspaceApiCore::ApiCommon
|
9
9
|
|
10
|
-
# @return [Smash] map state to valid internal values
|
11
|
-
SERVER_STATE_MAP = Smash.new(
|
12
|
-
'ACTIVE' => :running,
|
13
|
-
'DELETED' => :terminated,
|
14
|
-
'SUSPENDED' => :stopped,
|
15
|
-
'PASSWORD' => :running
|
16
|
-
)
|
17
|
-
|
18
|
-
def server_save(server)
|
19
|
-
unless(server.persisted?)
|
20
|
-
server.load_data(server.attributes)
|
21
|
-
result = request(
|
22
|
-
:expects => 202,
|
23
|
-
:method => :post,
|
24
|
-
:path => '/servers',
|
25
|
-
:json => {
|
26
|
-
:server => {
|
27
|
-
:flavorRef => server.flavor_id,
|
28
|
-
:name => server.name,
|
29
|
-
:imageRef => server.image_id,
|
30
|
-
:metadata => server.metadata,
|
31
|
-
:personality => server.personality,
|
32
|
-
:key_pair => server.key_name
|
33
|
-
}
|
34
|
-
}
|
35
|
-
)
|
36
|
-
server.id = result.get(:body, :server, :id)
|
37
|
-
else
|
38
|
-
raise "WAT DO I DO!?"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def server_destroy(server)
|
43
|
-
if(server.persisted?)
|
44
|
-
result = request(
|
45
|
-
:expects => 204,
|
46
|
-
:method => :delete,
|
47
|
-
:path => "/servers/#{server.id}"
|
48
|
-
)
|
49
|
-
else
|
50
|
-
raise "this doesn't even exist"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def server_change_state(server, state)
|
55
|
-
end
|
56
|
-
|
57
|
-
def server_reload(server)
|
58
|
-
res = servers.reload.all
|
59
|
-
node = res.detect do |s|
|
60
|
-
s.id == server.id
|
61
|
-
end
|
62
|
-
if(node)
|
63
|
-
server.load_data(node.data.dup)
|
64
|
-
server.valid_state
|
65
|
-
else
|
66
|
-
server.data[:state] = :terminated
|
67
|
-
server.dirty.clear
|
68
|
-
server
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def server_all
|
73
|
-
result = request(
|
74
|
-
:method => :get,
|
75
|
-
:path => '/servers/detail'
|
76
|
-
)
|
77
|
-
result[:body].fetch(:servers, []).map do |srv|
|
78
|
-
Server.new(
|
79
|
-
self,
|
80
|
-
:id => srv[:id],
|
81
|
-
:name => srv[:name],
|
82
|
-
:image_id => srv.get(:image, :id),
|
83
|
-
:flavor_id => srv.get(:flavor, :id),
|
84
|
-
:state => SERVER_STATE_MAP.fetch(srv[:status], :pending),
|
85
|
-
:addresses_private => srv.fetch(:addresses, :private, []).map{|a|
|
86
|
-
Server::Address.new(
|
87
|
-
:version => a[:version].to_i, :address => a[:addr]
|
88
|
-
)
|
89
|
-
},
|
90
|
-
:addresses_public => srv.fetch(:addresses, :public, []).map{|a|
|
91
|
-
Server::Address.new(
|
92
|
-
:version => a[:version].to_i, :address => a[:addr]
|
93
|
-
)
|
94
|
-
},
|
95
|
-
:status => srv[:status],
|
96
|
-
:key_name => srv[:key_name]
|
97
|
-
).valid_state
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
10
|
end
|
102
11
|
end
|
103
12
|
end
|
@@ -3,7 +3,7 @@ require 'miasma'
|
|
3
3
|
module Miasma
|
4
4
|
module Models
|
5
5
|
class Orchestration
|
6
|
-
class Rackspace <
|
6
|
+
class Rackspace < OpenStack
|
7
7
|
|
8
8
|
include Contrib::RackspaceApiCore::ApiCommon
|
9
9
|
|
@@ -19,236 +19,6 @@ module Miasma
|
|
19
19
|
)
|
20
20
|
)
|
21
21
|
|
22
|
-
# Save the stack
|
23
|
-
#
|
24
|
-
# @param stack [Models::Orchestration::Stack]
|
25
|
-
# @return [Models::Orchestration::Stack]
|
26
|
-
def stack_save(stack)
|
27
|
-
if(stack.persisted?)
|
28
|
-
stack.load_data(stack.attributes)
|
29
|
-
result = request(
|
30
|
-
:expects => 202,
|
31
|
-
:method => :put,
|
32
|
-
:path => "/stacks/#{stack.name}/#{stack.id}",
|
33
|
-
:json => {
|
34
|
-
:stack_name => stack.name,
|
35
|
-
:template => MultiJson.dump(stack.template),
|
36
|
-
:parameters => stack.parameters || {}
|
37
|
-
}
|
38
|
-
)
|
39
|
-
stack.valid_state
|
40
|
-
else
|
41
|
-
stack.load_data(stack.attributes)
|
42
|
-
result = request(
|
43
|
-
:expects => 201,
|
44
|
-
:method => :post,
|
45
|
-
:path => '/stacks',
|
46
|
-
:json => {
|
47
|
-
:stack_name => stack.name,
|
48
|
-
:template => MultiJson.dump(stack.template),
|
49
|
-
:parameters => stack.parameters || {},
|
50
|
-
:disable_rollback => (!!stack.disable_rollback).to_s
|
51
|
-
}
|
52
|
-
)
|
53
|
-
stack.id = result.get(:body, :stack, :id)
|
54
|
-
stack.valid_state
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Reload the stack data from the API
|
59
|
-
#
|
60
|
-
# @param stack [Models::Orchestration::Stack]
|
61
|
-
# @return [Models::Orchestration::Stack]
|
62
|
-
def stack_reload(stack)
|
63
|
-
if(stack.persisted?)
|
64
|
-
result = request(
|
65
|
-
:method => :get,
|
66
|
-
:path => "/stacks/#{stack.name}/#{stack.id}",
|
67
|
-
:expects => 200
|
68
|
-
)
|
69
|
-
stk = result.get(:body, :stack)
|
70
|
-
stack.load_data(
|
71
|
-
:id => stk[:id],
|
72
|
-
:capabilities => stk[:capabilities],
|
73
|
-
:creation_time => Time.parse(stk[:creation_time]),
|
74
|
-
:description => stk[:description],
|
75
|
-
:disable_rollback => stk[:disable_rollback].to_s.downcase == 'true',
|
76
|
-
:notification_topics => stk[:notification_topics],
|
77
|
-
:name => stk[:stack_name],
|
78
|
-
:state => stk[:stack_status].downcase.to_sym,
|
79
|
-
:status => stk[:stack_status],
|
80
|
-
:status_reason => stk[:stack_status_reason],
|
81
|
-
:template_description => stk[:template_description],
|
82
|
-
:timeout_in_minutes => stk[:timeout_mins].to_s.empty? ? nil : stk[:timeout_mins].to_i,
|
83
|
-
:updated_time => stk[:updated_time].to_s.empty? ? nil : Time.parse(stk[:updated_time]),
|
84
|
-
:parameters => stk.fetch(:parameters, Smash.new),
|
85
|
-
:outputs => stk.fetch(:outputs, []).map{ |output|
|
86
|
-
Smash.new(
|
87
|
-
:key => output[:output_key],
|
88
|
-
:value => output[:output_value],
|
89
|
-
:description => output[:description]
|
90
|
-
)
|
91
|
-
}
|
92
|
-
).valid_state
|
93
|
-
end
|
94
|
-
stack
|
95
|
-
end
|
96
|
-
|
97
|
-
# Delete the stack
|
98
|
-
#
|
99
|
-
# @param stack [Models::Orchestration::Stack]
|
100
|
-
# @return [TrueClass, FalseClass]
|
101
|
-
def stack_destroy(stack)
|
102
|
-
if(stack.persisted?)
|
103
|
-
request(
|
104
|
-
:method => :delete,
|
105
|
-
:path => "/stacks/#{stack.name}/#{stack.id}",
|
106
|
-
:expects => 204
|
107
|
-
)
|
108
|
-
true
|
109
|
-
else
|
110
|
-
false
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# Fetch stack template
|
115
|
-
#
|
116
|
-
# @param stack [Stack]
|
117
|
-
# @return [Smash] stack template
|
118
|
-
def stack_template_load(stack)
|
119
|
-
if(stack.persisted?)
|
120
|
-
result = request(
|
121
|
-
:method => :get,
|
122
|
-
:path => "/stacks/#{stack.name}/#{stack.id}/template"
|
123
|
-
)
|
124
|
-
result.fetch(:body, Smash.new)
|
125
|
-
else
|
126
|
-
Smash.new
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# Validate stack template
|
131
|
-
#
|
132
|
-
# @param stack [Stack]
|
133
|
-
# @return [NilClass, String] nil if valid, string error message if invalid
|
134
|
-
def stack_template_validate(stack)
|
135
|
-
begin
|
136
|
-
result = request(
|
137
|
-
:method => :post,
|
138
|
-
:path => '/validate',
|
139
|
-
:json => Smash.new(
|
140
|
-
:template => stack.template
|
141
|
-
)
|
142
|
-
)
|
143
|
-
nil
|
144
|
-
rescue Error::ApiError::RequestError => e
|
145
|
-
MultiJson.load(e.response.body.to_s).to_smash.get(:error, :message)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# Return all stacks
|
150
|
-
#
|
151
|
-
# @param options [Hash] filter
|
152
|
-
# @return [Array<Models::Orchestration::Stack>]
|
153
|
-
# @todo check if we need any mappings on state set
|
154
|
-
def stack_all(options={})
|
155
|
-
result = request(
|
156
|
-
:method => :get,
|
157
|
-
:path => '/stacks'
|
158
|
-
)
|
159
|
-
result.fetch(:body, :stacks, []).map do |s|
|
160
|
-
Stack.new(
|
161
|
-
self,
|
162
|
-
:id => s[:id],
|
163
|
-
:creation_time => Time.parse(s[:creation_time]),
|
164
|
-
:description => s[:description],
|
165
|
-
:name => s[:stack_name],
|
166
|
-
:state => s[:stack_status].downcase.to_sym,
|
167
|
-
:status => s[:stack_status],
|
168
|
-
:status_reason => s[:stack_status_reason],
|
169
|
-
:updated_time => s[:updated_time].to_s.empty? ? nil : Time.parse(s[:updated_time])
|
170
|
-
).valid_state
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# Return all resources for stack
|
175
|
-
#
|
176
|
-
# @param stack [Models::Orchestration::Stack]
|
177
|
-
# @return [Array<Models::Orchestration::Stack::Resource>]
|
178
|
-
def resource_all(stack)
|
179
|
-
result = request(
|
180
|
-
:method => :get,
|
181
|
-
:path => "/stacks/#{stack.name}/#{stack.id}/resources",
|
182
|
-
:expects => 200
|
183
|
-
)
|
184
|
-
result.fetch(:body, :resources, []).map do |resource|
|
185
|
-
Stack::Resource.new(
|
186
|
-
stack,
|
187
|
-
:id => resource[:physical_resource_id],
|
188
|
-
:name => resource[:resource_name],
|
189
|
-
:type => resource[:resource_type],
|
190
|
-
:logical_id => resource[:logical_resource_id],
|
191
|
-
:state => resource[:resource_status].downcase.to_sym,
|
192
|
-
:status => resource[:resource_status],
|
193
|
-
:status_reason => resource[:resource_status_reason],
|
194
|
-
:updated_time => Time.parse(resource[:updated_time])
|
195
|
-
).valid_state
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# Reload the stack resource data from the API
|
200
|
-
#
|
201
|
-
# @param resource [Models::Orchestration::Stack::Resource]
|
202
|
-
# @return [Models::Orchestration::Resource]
|
203
|
-
def resource_reload(resource)
|
204
|
-
resource.stack.resources.reload
|
205
|
-
resource.stack.resources.get(resource.id)
|
206
|
-
end
|
207
|
-
|
208
|
-
# Return all events for stack
|
209
|
-
#
|
210
|
-
# @param stack [Models::Orchestration::Stack]
|
211
|
-
# @return [Array<Models::Orchestration::Stack::Event>]
|
212
|
-
def event_all(stack, marker = nil)
|
213
|
-
params = marker ? {:marker => marker} : {}
|
214
|
-
result = request(
|
215
|
-
:path => "/stacks/#{stack.name}/#{stack.id}/events",
|
216
|
-
:method => :get,
|
217
|
-
:expects => 200,
|
218
|
-
:params => params
|
219
|
-
)
|
220
|
-
result.fetch(:body, :events, []).map do |event|
|
221
|
-
Stack::Event.new(
|
222
|
-
stack,
|
223
|
-
:id => event[:id],
|
224
|
-
:resource_id => event[:physical_resource_id],
|
225
|
-
:resource_name => event[:resource_name],
|
226
|
-
:resource_logical_id => event[:logical_resource_id],
|
227
|
-
:resource_state => event[:resource_status].downcase.to_sym,
|
228
|
-
:resource_status => event[:resource_status],
|
229
|
-
:resource_status_reason => event[:resource_status_reason],
|
230
|
-
:time => Time.parse(event[:event_time])
|
231
|
-
).valid_state
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
# Return all new events for event collection
|
236
|
-
#
|
237
|
-
# @param events [Models::Orchestration::Stack::Events]
|
238
|
-
# @return [Array<Models::Orchestration::Stack::Event>]
|
239
|
-
def event_all_new(events)
|
240
|
-
event_all(events.stack, events.all.first.id)
|
241
|
-
end
|
242
|
-
|
243
|
-
# Reload the stack event data from the API
|
244
|
-
#
|
245
|
-
# @param resource [Models::Orchestration::Stack::Event]
|
246
|
-
# @return [Models::Orchestration::Event]
|
247
|
-
def event_reload(event)
|
248
|
-
event.stack.events.reload
|
249
|
-
event.stack.events.get(event.id)
|
250
|
-
end
|
251
|
-
|
252
22
|
end
|
253
23
|
end
|
254
24
|
end
|