abiquo-api 0.0.1
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 +7 -0
- data/.gitignore +34 -0
- data/Gemfile +3 -0
- data/README.md +166 -0
- data/abiquo-api.gemspec +22 -0
- data/lib/abiquo-api.rb +237 -0
- data/lib/abiquo-api/errors.rb +41 -0
- data/lib/abiquo-api/httpclient.rb +165 -0
- data/lib/abiquo-api/link.rb +94 -0
- data/lib/abiquo-api/model.rb +194 -0
- data/lib/abiquo-api/version.rb +3 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1304f42a84ccb9a8c9f92e6b8e6b41e9f4a4c509
|
4
|
+
data.tar.gz: 0ebab054e7d0e287045ac821e171c8d2e6cbe969
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 85c7aa06dafb2cf62ca1c6f6312c2134e469919acd2233421ea5446847b2d292a7b8ff1005b3976ab566b54c59bc36f0c0580317e024e54d8910c04a4d7223e0
|
7
|
+
data.tar.gz: 8828b536738f0d636ef4ec1426b4392b4a4682e7452c72b0f2fc2b74246470e41b3908335afeef567c0bc996fd1562287023f70aeba7a907e8b090f5a8dd2a58
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
Gemfile.lock
|
30
|
+
.ruby-version
|
31
|
+
.ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# Abiquo API client for Ruby
|
2
|
+
|
3
|
+
Basic API browsing and raw object manipulation for Ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
To get the client installed just issue:
|
8
|
+
|
9
|
+
```gem install abiquo-api```
|
10
|
+
|
11
|
+
Or if you are using a Gemfile, add:
|
12
|
+
|
13
|
+
```gem 'abiquo-api'```
|
14
|
+
|
15
|
+
An example usage would be:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
require 'abiquo-api'
|
19
|
+
|
20
|
+
a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
|
21
|
+
:abiquo_username => "admin",
|
22
|
+
:abiquo_password => "xabiquo")
|
23
|
+
```
|
24
|
+
|
25
|
+
Or, if you want to force a specific API version:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'abiquo-api'
|
29
|
+
|
30
|
+
a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
|
31
|
+
:abiquo_username => "admin",
|
32
|
+
:abiquo_password => "xabiquo",
|
33
|
+
:version => "2.9")
|
34
|
+
```
|
35
|
+
|
36
|
+
Then you can start browsing the API:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
l = AbiquoAPI::Link.new(:href => '/api/cloud/virtualdatacenters',
|
40
|
+
:type => 'application/vnd.abiquo.virtualdatacenters+json')
|
41
|
+
|
42
|
+
a.get(l)
|
43
|
+
```
|
44
|
+
|
45
|
+
## Client object
|
46
|
+
|
47
|
+
The client object contains 3 objects that allow API browsing.
|
48
|
+
|
49
|
+
- **user.** The user object returned by the ```/api/login``` call.
|
50
|
+
- **enterprise.** The user's enterprise link to be used in new objects.
|
51
|
+
- **properties.** A hash containing all the system properties in the system. Useful to get default values for some objects (ie. VLAN parameters in VDC creation).
|
52
|
+
|
53
|
+
## Link object
|
54
|
+
|
55
|
+
Represents an Abiquo API Link. Issuing ```get``` on them will retrieve link destination. This allows for things like:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
vapp = vdc.link(:virtualappliances).get.first
|
59
|
+
```
|
60
|
+
|
61
|
+
## Generic model object
|
62
|
+
|
63
|
+
This is used to map Abiquo API objects.
|
64
|
+
|
65
|
+
## Examples
|
66
|
+
|
67
|
+
### Browse the API
|
68
|
+
|
69
|
+
#### Initialize connection
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
|
73
|
+
:abiquo_username => "admin",
|
74
|
+
:abiquo_password => "xabiquo")
|
75
|
+
```
|
76
|
+
|
77
|
+
|
78
|
+
#### User object
|
79
|
+
|
80
|
+
Is the User object returned by the API at login. You can browse the links provided like:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
a.user
|
84
|
+
|
85
|
+
vm = a.user.link(:virtualmachines).get.first
|
86
|
+
|
87
|
+
vm.name
|
88
|
+
=> "ABQ_6b6d9856-c05f-425e-8916-1ff7de1683e3"
|
89
|
+
|
90
|
+
vm.id
|
91
|
+
=> 18
|
92
|
+
```
|
93
|
+
|
94
|
+
### Create a VDC using an existing one as reference
|
95
|
+
|
96
|
+
#### Initialize connection
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
|
100
|
+
:abiquo_username => "admin",
|
101
|
+
:abiquo_password => "xabiquo")
|
102
|
+
```
|
103
|
+
|
104
|
+
#### Create a Link object to issue a request
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
l = AbiquoAPI::Link.new(:href => '/api/cloud/virtualdatacenters',
|
108
|
+
:type => 'application/vnd.abiquo.virtualdatacenters+json')
|
109
|
+
```
|
110
|
+
|
111
|
+
#### Get on the link
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
v = a.get(l).first
|
115
|
+
```
|
116
|
+
|
117
|
+
#### Create a new object
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
v1 = a.new_object(:name => "vdctest",
|
121
|
+
:hypervisorType => "VMX_04",
|
122
|
+
:vlan => v.vlan,
|
123
|
+
:links => [v.link(:location), a.enterprise])
|
124
|
+
v1.vlan.delete("links")
|
125
|
+
v1.vlan.delete("id")
|
126
|
+
v1.vlan.delete("tag")
|
127
|
+
```
|
128
|
+
|
129
|
+
#### Create a link where to post data
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
l1 = AbiquoAPI::Link.new(:href => '/api/cloud/virtualdatacenters',
|
133
|
+
:type => 'application/vnd.abiquo.virtualdatacenter+json')
|
134
|
+
```
|
135
|
+
|
136
|
+
#### Post data
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
v2 = a.post(l1, v1)
|
140
|
+
```
|
141
|
+
|
142
|
+
#### Modify the created object
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
v2.name = "SomeValue"
|
146
|
+
v2 = a.put(v2.edit, v2)
|
147
|
+
```
|
148
|
+
|
149
|
+
Or:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
v2.name = "SomeValue"
|
153
|
+
v2.update
|
154
|
+
```
|
155
|
+
|
156
|
+
#### Delete it
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
a.delete(v2.edit)
|
160
|
+
```
|
161
|
+
|
162
|
+
Or:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
v2.delete
|
166
|
+
```
|
data/abiquo-api.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'abiquo-api/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "abiquo-api"
|
8
|
+
gem.version = AbiquoAPIClient::VERSION
|
9
|
+
gem.authors = ["Marc Cirauqui"]
|
10
|
+
gem.email = ["marc.cirauqui@abiquo.com"]
|
11
|
+
gem.description = %q{Simple Abiquo API client}
|
12
|
+
gem.homepage = "https://github.com/abiquo/api-ruby"
|
13
|
+
gem.summary = gem.description
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.add_runtime_dependency "excon", '~> 0.43', '>= 0.43.0'
|
20
|
+
gem.add_runtime_dependency "formatador", '~> 0.2', '>= 0.2.5'
|
21
|
+
gem.add_runtime_dependency "json", '~> 1.8', '>= 1.8.0'
|
22
|
+
end
|
data/lib/abiquo-api.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'abiquo-api/errors.rb'
|
2
|
+
require 'abiquo-api/httpclient.rb'
|
3
|
+
require 'abiquo-api/link.rb'
|
4
|
+
require 'abiquo-api/model.rb'
|
5
|
+
|
6
|
+
##
|
7
|
+
# Ruby Abiquo API client main class
|
8
|
+
#
|
9
|
+
class AbiquoAPI
|
10
|
+
include AbiquoAPIClient
|
11
|
+
|
12
|
+
##
|
13
|
+
# The {AbiquoAPIClient::HTTPClient} used by
|
14
|
+
# this instance.
|
15
|
+
#
|
16
|
+
attr_reader :http_client
|
17
|
+
|
18
|
+
##
|
19
|
+
# A hash of the enterprise to which the user
|
20
|
+
# belongs to.
|
21
|
+
#
|
22
|
+
attr_accessor :enterprise
|
23
|
+
|
24
|
+
##
|
25
|
+
# An instance of {AbiquoAPIClient::LinkModel} representing
|
26
|
+
# the current user.
|
27
|
+
#
|
28
|
+
attr_accessor :user
|
29
|
+
|
30
|
+
##
|
31
|
+
# The config properties for the UI.
|
32
|
+
#
|
33
|
+
attr_accessor :properties
|
34
|
+
|
35
|
+
##
|
36
|
+
# The Abiquo API version used by this client.
|
37
|
+
#
|
38
|
+
attr_accessor :version
|
39
|
+
|
40
|
+
##
|
41
|
+
# Initializes a new AbiquoAPI instance.
|
42
|
+
#
|
43
|
+
# Required options:
|
44
|
+
#
|
45
|
+
# :abiquo_api_url => The URL of the Abiquo API. ie. https://yourserver/api
|
46
|
+
# :abiquo_username => The username used to connect to the Abiquo API.
|
47
|
+
# :abiquo_password => The password for your user.
|
48
|
+
# :version => The Abiquo API version to include in each request.
|
49
|
+
# Defaults to whatever is returned in the /api/version resource
|
50
|
+
#
|
51
|
+
def initialize(options = {})
|
52
|
+
api_url = options[:abiquo_api_url]
|
53
|
+
api_username = options[:abiquo_username]
|
54
|
+
api_password = options[:abiquo_password]
|
55
|
+
|
56
|
+
raise "You need to set :abiquo_api_url, :abiquo_username and :abiquo_password" if api_url.nil? or api_username.nil? or api_password.nil?
|
57
|
+
|
58
|
+
@http_client = AbiquoAPIClient::HTTPClient.new(api_url,
|
59
|
+
api_username,
|
60
|
+
api_password)
|
61
|
+
api_path = URI.parse(api_url).path
|
62
|
+
|
63
|
+
loginresp = @http_client.request(
|
64
|
+
:expects => [200],
|
65
|
+
:method => 'GET',
|
66
|
+
:path => "#{api_path}/login",
|
67
|
+
:accept => 'application/vnd.abiquo.user+json'
|
68
|
+
)
|
69
|
+
@enterprise = AbiquoAPIClient::Link.new(loginresp['links'].select {|l| l['rel'] == 'enterprise'}.first)
|
70
|
+
@user = AbiquoAPIClient::LinkModel.new(loginresp.merge({:client => self}))
|
71
|
+
|
72
|
+
@properties = @http_client.request(
|
73
|
+
:expects => [200],
|
74
|
+
:method => 'GET',
|
75
|
+
:path => "#{api_path}/config/properties",
|
76
|
+
:accept => 'application/vnd.abiquo.systemproperties+json'
|
77
|
+
)
|
78
|
+
|
79
|
+
if options.has_key? :version
|
80
|
+
@version = options[:version][0..2]
|
81
|
+
else
|
82
|
+
@version = @http_client.request(
|
83
|
+
:expects => [200],
|
84
|
+
:method => 'GET',
|
85
|
+
:path => "#{api_path}/version",
|
86
|
+
:accept => 'text/plain'
|
87
|
+
).delete("\n")[0..2]
|
88
|
+
end
|
89
|
+
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Returns a new instance of the {AbiquoAPIClient::LinkModel} class.
|
95
|
+
#
|
96
|
+
# Parameters:
|
97
|
+
# A hash of attributes to set in the object.
|
98
|
+
#
|
99
|
+
def new_object(hash)
|
100
|
+
AbiquoAPIClient::LinkModel.new(hash.merge({ :client => self}))
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Executes an HTTP GET over the {AbiquoAPIClient::Link} passed as parameter.
|
105
|
+
#
|
106
|
+
# Required parameters:
|
107
|
+
# [link] An instance
|
108
|
+
# of an {AbiquoAPIClient::Link}.
|
109
|
+
#
|
110
|
+
# Optional parameters:
|
111
|
+
# [options] A hash of key/values that will
|
112
|
+
# be sent as query.
|
113
|
+
#
|
114
|
+
# **NOTE:** The option :accept will override Accept header sent in
|
115
|
+
# the request.
|
116
|
+
#
|
117
|
+
# Returns an instance of the {AbiquoAPIClient::LinkModel} class representing
|
118
|
+
# the requested resource.
|
119
|
+
#
|
120
|
+
def get(link, options = {})
|
121
|
+
accept = options[:accept].nil? ? link.type : options.delete(:accept)
|
122
|
+
|
123
|
+
req_hash = {
|
124
|
+
:expects => [200],
|
125
|
+
:method => 'GET',
|
126
|
+
:path => link.href,
|
127
|
+
:query => options
|
128
|
+
}
|
129
|
+
|
130
|
+
req_hash[:accept] = "#{accept}; version=#{@version};" unless accept.eql? ""
|
131
|
+
|
132
|
+
resp = @http_client.request(req_hash)
|
133
|
+
|
134
|
+
if resp.is_a? Array
|
135
|
+
tmp_a = []
|
136
|
+
resp.each do |r|
|
137
|
+
tmp_r = AbiquoAPIClient::LinkModel.new(r.merge({:client => self}))
|
138
|
+
tmp_a << tmp_r
|
139
|
+
end
|
140
|
+
tmp_a
|
141
|
+
else
|
142
|
+
AbiquoAPIClient::LinkModel.new(resp.merge({ :client => self}))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Executes an HTTP POST over the {AbiquoAPIClient::Link} passed as parameter.
|
148
|
+
#
|
149
|
+
# Required parameters:
|
150
|
+
# [link] An instance of an {AbiquoAPIClient::Link}.
|
151
|
+
# [data] The data to send in the HTTP request. Usually an instance
|
152
|
+
# of the {AbiquoAPIClient::LinkModel} instance. Will be
|
153
|
+
# serialized to JSON before sending.
|
154
|
+
#
|
155
|
+
# Optional parameters:
|
156
|
+
# [options] A hash of key/values that will be sent as query.
|
157
|
+
#
|
158
|
+
# **NOTE:** The option :accept and :content options will override Accept
|
159
|
+
# and Content-Type headers sent in the request.
|
160
|
+
#
|
161
|
+
# Returns an instance of the {AbiquoAPIClient::LinkModel} class representing
|
162
|
+
# the requested resource or nil if the request returned empty.
|
163
|
+
#
|
164
|
+
def post(link, data, options = {})
|
165
|
+
ctype = options[:content].nil? ? link.type : options.delete(:content)
|
166
|
+
accept = options[:accept].nil? ? link.type : options.delete(:accept)
|
167
|
+
|
168
|
+
req_hash = {
|
169
|
+
:method => 'POST',
|
170
|
+
:path => link.href,
|
171
|
+
:body => data.to_json,
|
172
|
+
:query => options
|
173
|
+
}
|
174
|
+
|
175
|
+
req_hash[:accept] = "#{accept}; version=#{@version};" unless accept.eql? ""
|
176
|
+
req_hash[:content] = "#{ctype}; version=#{@version};" unless ctype.eql? ""
|
177
|
+
|
178
|
+
resp = @http_client.request(req_hash)
|
179
|
+
resp.nil? ? nil : AbiquoAPIClient::LinkModel.new({ :client => self}.merge(resp))
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Executes an HTTP PUT over the {AbiquoAPIClient::Link} passed as parameter.
|
184
|
+
#
|
185
|
+
# Required parameters:
|
186
|
+
# [link] An instance of an {AbiquoAPIClient::Link}.
|
187
|
+
# [data] The data to send in the HTTP request. Usually an instance
|
188
|
+
# of the {AbiquoAPIClient::LinkModel} instance. Will be
|
189
|
+
# serialized to JSON before sending.
|
190
|
+
#
|
191
|
+
# Optional parameters:
|
192
|
+
# [options] A hash of key/values that will be sent as query.
|
193
|
+
#
|
194
|
+
# **NOTE:** The option :accept and :content options will override Accept
|
195
|
+
# and Content-Type headers sent in the request.
|
196
|
+
#
|
197
|
+
# Returns an instance of the {AbiquoAPIClient::LinkModel} class representing
|
198
|
+
# the requested resource or nil if the request returned empty.
|
199
|
+
#
|
200
|
+
def put(link, data, options = {})
|
201
|
+
ctype = options[:content].nil? ? link.type : options.delete(:content)
|
202
|
+
accept = options[:accept].nil? ? link.type : options.delete(:accept)
|
203
|
+
|
204
|
+
req_hash = {
|
205
|
+
:method => 'PUT',
|
206
|
+
:path => link.href,
|
207
|
+
:body => data.to_json,
|
208
|
+
:query => options
|
209
|
+
}
|
210
|
+
|
211
|
+
req_hash[:accept] = "#{accept}; version=#{@version};" unless accept.eql? ""
|
212
|
+
req_hash[:content] = "#{ctype}; version=#{@version};" unless ctype.eql? ""
|
213
|
+
|
214
|
+
resp = @http_client.request(req_hash)
|
215
|
+
resp.nil? ? nil : AbiquoAPIClient::LinkModel.new({ :client => self}.merge(resp))
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# Executes an HTTP DELETE over the {AbiquoAPIClient::Link} passed as parameter.
|
220
|
+
#
|
221
|
+
# Required parameters:
|
222
|
+
# [link] An instance of an {AbiquoAPIClient::Link}.
|
223
|
+
#
|
224
|
+
# Optional parameters:
|
225
|
+
# [options] A hash of key/values that will be sent as query.
|
226
|
+
#
|
227
|
+
# Returns nil
|
228
|
+
#
|
229
|
+
def delete(link, options = {})
|
230
|
+
@http_client.request(
|
231
|
+
:expects => [204],
|
232
|
+
:method => 'DELETE',
|
233
|
+
:path => link.href
|
234
|
+
)
|
235
|
+
nil
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AbiquoAPIClient
|
2
|
+
##
|
3
|
+
# Common error exception
|
4
|
+
#
|
5
|
+
class Error < Exception; end
|
6
|
+
|
7
|
+
##
|
8
|
+
# InvalidCredentials exception.
|
9
|
+
# Raised whenever the API responds with
|
10
|
+
# a 401 HTTP code.
|
11
|
+
#
|
12
|
+
class InvalidCredentials < AbiquoAPIClient::Error; end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Forbidden exception
|
16
|
+
# Raised whenever the API responds with
|
17
|
+
# a 403 HTTP code.
|
18
|
+
#
|
19
|
+
class Forbidden < AbiquoAPIClient::Error; end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Badrequest exception
|
23
|
+
# Raised whenever the API responds with
|
24
|
+
# a 400 or 406 HTTP code.
|
25
|
+
#
|
26
|
+
class BadRequest < AbiquoAPIClient::Error; end
|
27
|
+
|
28
|
+
##
|
29
|
+
# NotFound exception
|
30
|
+
# Raised whenever the API responds with
|
31
|
+
# a 404 HTTP code.
|
32
|
+
#
|
33
|
+
class NotFound < AbiquoAPIClient::Error; end
|
34
|
+
|
35
|
+
##
|
36
|
+
# UnsupportedMediaType exception
|
37
|
+
# Raised whenever the API responds with
|
38
|
+
# a 415 HTTP code.
|
39
|
+
#
|
40
|
+
class UnsupportedMediaType < AbiquoAPIClient::Error; end
|
41
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'excon'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module AbiquoAPIClient
|
6
|
+
##
|
7
|
+
# HTTPClient class.
|
8
|
+
#
|
9
|
+
# Does the actual HTTP requests to the server.
|
10
|
+
#
|
11
|
+
class HTTPClient
|
12
|
+
##
|
13
|
+
# Excon connection object.
|
14
|
+
#
|
15
|
+
attr_reader :connection
|
16
|
+
|
17
|
+
##
|
18
|
+
# Cookies returned by the API. Contains the auth
|
19
|
+
# cookie that will be used after the first call.
|
20
|
+
#
|
21
|
+
attr_reader :cookies
|
22
|
+
|
23
|
+
##
|
24
|
+
# Constructor. Recieves the parameters to establish
|
25
|
+
# a connection to the Abiquo API.
|
26
|
+
#
|
27
|
+
# Parameters:
|
28
|
+
# :abiquo_api_url:: The URL of the Abiquo API. ie. https://yourserver/api
|
29
|
+
# :abiquo_username:: The username used to connect to the Abiquo API.
|
30
|
+
# :abiquo_password:: The password for your user.
|
31
|
+
#
|
32
|
+
def initialize(api_url, api_username, api_password)
|
33
|
+
Excon.defaults[:ssl_verify_peer] = false
|
34
|
+
@connection = Excon.new(api_url,
|
35
|
+
:user => api_username,
|
36
|
+
:password => api_password )
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# The public method called by the client.
|
42
|
+
#
|
43
|
+
# Parameters:
|
44
|
+
# [params] A hash of options to be passed to the
|
45
|
+
# Excon connection.
|
46
|
+
#
|
47
|
+
# Returns a hash resulting of parsing the response
|
48
|
+
# body as JSON, or nil if the response is "No
|
49
|
+
# content" (HTTP code 204).
|
50
|
+
#
|
51
|
+
def request(params)
|
52
|
+
# Remove nil params
|
53
|
+
params.reject!{|k,v| v.nil?}
|
54
|
+
|
55
|
+
# Setup Accept and Content-Type headers
|
56
|
+
headers={}
|
57
|
+
headers.merge!('Accept' => params.delete(:accept)) if params.has_key?(:accept)
|
58
|
+
headers.merge!('Content-Type' => params.delete(:content)) if params.has_key?(:content)
|
59
|
+
|
60
|
+
# Set Auth cookie and delete user and password if present
|
61
|
+
@connection.data.delete(:user) unless @connection.data[:user].nil?
|
62
|
+
@connection.data.delete(:password) unless @connection.data[:password].nil?
|
63
|
+
headers.merge!(@cookies) unless @cookies.nil?
|
64
|
+
|
65
|
+
params[:headers] = headers
|
66
|
+
|
67
|
+
# Correct API path
|
68
|
+
path = URI.parse(params[:path]).path
|
69
|
+
params[:path] = path
|
70
|
+
|
71
|
+
response = issue_request(params)
|
72
|
+
return nil if response.nil?
|
73
|
+
|
74
|
+
begin
|
75
|
+
response = JSON.parse(response.body) unless response.body.empty?
|
76
|
+
rescue
|
77
|
+
response = response.body
|
78
|
+
end
|
79
|
+
|
80
|
+
# Handle pagination
|
81
|
+
if not response['links'].nil? and response['links'].select {|l| l['rel'].eql? "next" }.count > 0
|
82
|
+
items = []
|
83
|
+
items = items + response['collection'] if not response['collection'].nil?
|
84
|
+
|
85
|
+
loop do
|
86
|
+
next_url = response['links'].select {|l| l['rel'].eql? "next" }.first['href']
|
87
|
+
uri = URI.parse(next_url)
|
88
|
+
params[:path] = uri.path
|
89
|
+
params[:query] = uri.query
|
90
|
+
params[:headers] = headers
|
91
|
+
response = issue_request(params)
|
92
|
+
response = JSON.parse(response.body) unless response.body.empty?
|
93
|
+
items = items + response['collection'] if not response['collection'].nil?
|
94
|
+
|
95
|
+
break if response['links'].select {|l| l['rel'].eql? "next" }.count == 0
|
96
|
+
end
|
97
|
+
|
98
|
+
items
|
99
|
+
else
|
100
|
+
if not response['collection'].nil?
|
101
|
+
response['collection']
|
102
|
+
else
|
103
|
+
response.nil? ? nil : response
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
##
|
111
|
+
# Issues the HTTP request using the Excon connection
|
112
|
+
# object.
|
113
|
+
#
|
114
|
+
# Handles API error codes.
|
115
|
+
#
|
116
|
+
def issue_request(options)
|
117
|
+
begin
|
118
|
+
resp = @connection.request(options)
|
119
|
+
|
120
|
+
# Save cookies
|
121
|
+
# Get all "Set-Cookie" headers and replace them with "Cookie" header.
|
122
|
+
@cookies = Hash[resp.headers.select{|k| k.eql? "Set-Cookie" }.map {|k,v| ["Cookie", v] }]
|
123
|
+
|
124
|
+
if resp.data[:status] == 204
|
125
|
+
nil
|
126
|
+
else
|
127
|
+
resp
|
128
|
+
end
|
129
|
+
rescue Excon::Errors::HTTPStatusError => error
|
130
|
+
case error.response.status
|
131
|
+
when 401
|
132
|
+
raise AbiquoAPIClient::InvalidCredentials, "Wrong username or password"
|
133
|
+
when 403
|
134
|
+
raise AbiquoAPIClient::Forbidden, "Not Authorized"
|
135
|
+
when 406, 400
|
136
|
+
raise AbiquoAPIClient::BadRequest, "Bad request"
|
137
|
+
when 415
|
138
|
+
raise AbiquoAPIClient::UnsupportedMediaType, "Unsupported mediatype"
|
139
|
+
when 404
|
140
|
+
begin
|
141
|
+
error_response = JSON.parse(error.response.body)
|
142
|
+
|
143
|
+
error_code = error_response['collection'][0]['code']
|
144
|
+
error_text = error_response['collection'][0]['message']
|
145
|
+
|
146
|
+
rescue
|
147
|
+
raise AbiquoAPIClient::NotFound, "Not Found; #{error_code} - #{error_text}"
|
148
|
+
end
|
149
|
+
raise AbiquoAPIClient::NotFound, "Not Found"
|
150
|
+
else
|
151
|
+
begin
|
152
|
+
error_response = JSON.parse(error.response.body)
|
153
|
+
|
154
|
+
error_code = error_response['collection'][0]['code']
|
155
|
+
error_text = error_response['collection'][0]['message']
|
156
|
+
|
157
|
+
rescue
|
158
|
+
raise AbiquoAPIClient::Error, error.response.body
|
159
|
+
end
|
160
|
+
raise AbiquoAPIClient::Error, "#{error_code} - #{error_text}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'formatador'
|
2
|
+
|
3
|
+
module AbiquoAPIClient
|
4
|
+
##
|
5
|
+
# Represents a link on the Abiquo API.
|
6
|
+
#
|
7
|
+
class Link
|
8
|
+
##
|
9
|
+
# The target URL of the link
|
10
|
+
#
|
11
|
+
attr_accessor :href
|
12
|
+
##
|
13
|
+
# The 'rel' attribute of the link
|
14
|
+
#
|
15
|
+
attr_accessor :rel
|
16
|
+
##
|
17
|
+
# The title of the link
|
18
|
+
#
|
19
|
+
attr_accessor :title
|
20
|
+
##
|
21
|
+
# The media type of the link
|
22
|
+
#
|
23
|
+
attr_accessor :type
|
24
|
+
|
25
|
+
##
|
26
|
+
# Constructor.
|
27
|
+
#
|
28
|
+
# Accepts a hash reprsenting a link, usually returned
|
29
|
+
# after parsing the JSON response.
|
30
|
+
#
|
31
|
+
# If the hash contains :client key, the value will be used
|
32
|
+
# as an {AbiquoAPI} client allowing the get method to retrieve
|
33
|
+
# the target resource.
|
34
|
+
#
|
35
|
+
def initialize(hash)
|
36
|
+
@client = hash.delete(:client) if hash.keys.include?(:client)
|
37
|
+
|
38
|
+
h = Hash[hash.map {|k, v| [k.to_sym, v ] }]
|
39
|
+
|
40
|
+
@href = h[:href].nil? ? '' : h[:href]
|
41
|
+
@rel = h[:rel].nil? ? '' : h[:rel]
|
42
|
+
@title = h[:title].nil? ? '' : h[:title]
|
43
|
+
@type = h[:type].nil? ? '' : h[:type]
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# If the :client attribute is not nil, will retrieve
|
48
|
+
# the resource that this link represents, or nil otherwise
|
49
|
+
#
|
50
|
+
def get
|
51
|
+
if @client.nil?
|
52
|
+
return nil
|
53
|
+
else
|
54
|
+
@client.get(self)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Converts an instance to its hash form, so it can be
|
60
|
+
# serialized as JSON.
|
61
|
+
#
|
62
|
+
def to_hash
|
63
|
+
h = self.href.nil? ? '' : self.href
|
64
|
+
r = self.rel.nil? ? '' : self.rel
|
65
|
+
t = self.title.nil? ? '' : self.title
|
66
|
+
y = self.type.nil? ? '' : self.type
|
67
|
+
|
68
|
+
{
|
69
|
+
"href" => h,
|
70
|
+
"type" => y,
|
71
|
+
"rel" => r,
|
72
|
+
"title" => t
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Pretty print the object.
|
78
|
+
#
|
79
|
+
def inspect
|
80
|
+
Thread.current[:formatador] ||= Formatador.new
|
81
|
+
data = "#{Thread.current[:formatador].indentation}<#{self.class.name}"
|
82
|
+
Thread.current[:formatador].indent do
|
83
|
+
unless self.instance_variables.empty?
|
84
|
+
vars = self.instance_variables.clone
|
85
|
+
vars.delete(:@client)
|
86
|
+
data << " "
|
87
|
+
data << vars.map { |v| "#{v}=#{instance_variable_get(v.to_s).inspect}" }.join(", ")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
data << " >"
|
91
|
+
data
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'formatador'
|
2
|
+
|
3
|
+
module AbiquoAPIClient
|
4
|
+
##
|
5
|
+
# Represents a resource in the Abiquo API.
|
6
|
+
#
|
7
|
+
class LinkModel
|
8
|
+
##
|
9
|
+
# Constructor
|
10
|
+
#
|
11
|
+
# Accepts a hash of key values representing the resource, plus
|
12
|
+
# an instance of the AbiquoAPI class to be used to execute
|
13
|
+
# the HTTP requests, specified as the :client attribute.
|
14
|
+
#
|
15
|
+
def initialize(attrs={})
|
16
|
+
raise "Needs a connection!" if attrs[:client].nil?
|
17
|
+
@client = attrs.delete(:client)
|
18
|
+
|
19
|
+
attributes = Hash[attrs.clone.map {|k, v| [k.to_s, v ] }]
|
20
|
+
|
21
|
+
if not attributes['links'].nil?
|
22
|
+
links = []
|
23
|
+
|
24
|
+
attributes['links'].each do |link|
|
25
|
+
link = link.to_hash if link.is_a? AbiquoAPIClient::Link
|
26
|
+
new_lnk = {}
|
27
|
+
|
28
|
+
if 'edit'.eql?(link['rel']) or 'self'.eql?(link['rel'])
|
29
|
+
# Create a URL string attribute
|
30
|
+
rel = 'url'
|
31
|
+
create_attr(rel, true)
|
32
|
+
instance_variable_set("@#{rel}", link['href'])
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create new getters and setters
|
36
|
+
# Also sets value to a Link object
|
37
|
+
rel = "#{link['rel'].gsub(/\//, '_')}"
|
38
|
+
new_lnk[rel.to_sym] = Link.new(link.merge({:client => @client}))
|
39
|
+
links << new_lnk
|
40
|
+
|
41
|
+
# For every link that points to an ID
|
42
|
+
# create a getter
|
43
|
+
if link['href'].split('/').last.is_a? Integer
|
44
|
+
idrel = "#{link['rel'].gsub(/\//, '_')}_id"
|
45
|
+
create_attr(idrel, true)
|
46
|
+
instance_variable_set("@#{idrel}", link['href'].split('/').last.to_i)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
attributes.delete('links')
|
50
|
+
|
51
|
+
create_attr("links")
|
52
|
+
instance_variable_set("@links", links)
|
53
|
+
|
54
|
+
# Now create getters and setters for every method
|
55
|
+
attributes.keys.each do |k|
|
56
|
+
create_attr(k)
|
57
|
+
instance_variable_set("@#{k}", attributes[k])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Serializes the object into a valid JSON for the Abiquo API.
|
64
|
+
#
|
65
|
+
def to_json
|
66
|
+
att = self.instance_variables.map {|v| v.to_s }
|
67
|
+
links = []
|
68
|
+
data = {}
|
69
|
+
|
70
|
+
att.delete("@url")
|
71
|
+
att.delete("@client")
|
72
|
+
|
73
|
+
self.links.each do |l|
|
74
|
+
links << l.values.first.to_hash
|
75
|
+
end
|
76
|
+
att.delete("@links")
|
77
|
+
|
78
|
+
att.each do |opt|
|
79
|
+
data[opt.delete("@")] = instance_variable_get(opt)
|
80
|
+
end
|
81
|
+
data['links'] = links
|
82
|
+
data.to_json
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Pretty print an instance object.
|
87
|
+
#
|
88
|
+
def inspect
|
89
|
+
Thread.current[:formatador] ||= Formatador.new
|
90
|
+
data = "#{Thread.current[:formatador].indentation}<#{self.class.name}"
|
91
|
+
Thread.current[:formatador].indent do
|
92
|
+
unless self.instance_variables.empty?
|
93
|
+
vars = self.instance_variables.clone
|
94
|
+
vars.delete(:@client)
|
95
|
+
data << "\n"
|
96
|
+
data << vars.map { |v| "#{v}=#{instance_variable_get(v.to_s).inspect}" }.join(",\n#{Thread.current[:formatador].indentation}")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
data << "\n#{Thread.current[:formatador].indentation}>"
|
100
|
+
data
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Retrieves the link that has the 'rel' attribute specified
|
105
|
+
# as parameter.
|
106
|
+
#
|
107
|
+
# Parameters:
|
108
|
+
# [link_rel] The 'rel' value to look for, symbolized.
|
109
|
+
#
|
110
|
+
# Returns the first link found with the 'rel' attribute
|
111
|
+
# specified or nil if not found.
|
112
|
+
#
|
113
|
+
def link(link_rel)
|
114
|
+
self.links.select {|l| l[link_rel] }.first[link_rel]
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Checks if the object has a link with the 'rel' attribute
|
119
|
+
# specified as parameter.
|
120
|
+
#
|
121
|
+
# Parameters:
|
122
|
+
# [link_rel] The 'rel' value to look for, symbolized.
|
123
|
+
#
|
124
|
+
# Returns the true if the object has a link with the
|
125
|
+
# specified 'rel' or false otherwhise.
|
126
|
+
#
|
127
|
+
def has_link?(link_rel)
|
128
|
+
c = self.links.select {|l| l[link_rel] }.count
|
129
|
+
c == 0 ? false : true
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Executes an HTTP PUT over the resource in Abiquo API,
|
134
|
+
# sending the current attributes as data.
|
135
|
+
#
|
136
|
+
# Returns a new instance representing the updated resource.
|
137
|
+
#
|
138
|
+
def update
|
139
|
+
@client.put(self.link(:edit), self)
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Executes an HTTP DELETE over the resource in Abiquo API,
|
144
|
+
# deleting the current resource.
|
145
|
+
#
|
146
|
+
# Returns nil on success.
|
147
|
+
#
|
148
|
+
def delete
|
149
|
+
@client.delete(self.link(:edit))
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Executes an HTTP GET over the resource in Abiquo API.
|
154
|
+
#
|
155
|
+
# Returns a new instance representing resource.
|
156
|
+
#
|
157
|
+
def refresh
|
158
|
+
self.link(:edit).get
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
##
|
164
|
+
# Creates a new method in the instance object.
|
165
|
+
#
|
166
|
+
# Parameters:
|
167
|
+
# [name] The name of the method to be created.
|
168
|
+
# [&block] The block of code for that method.
|
169
|
+
#
|
170
|
+
def create_method( name, &block )
|
171
|
+
self.class.send( :define_method, name, &block )
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Creates a new attribute for the instance object.
|
176
|
+
#
|
177
|
+
# Parameters:
|
178
|
+
# [name] The name of the attribute to be created.
|
179
|
+
# [ro] Boolean that specifies if the attribute will
|
180
|
+
# read only or read write. Defaults to false (rw)
|
181
|
+
#
|
182
|
+
def create_attr( name , ro = false)
|
183
|
+
unless ro
|
184
|
+
create_method( "#{name}=".to_sym ) { |val|
|
185
|
+
instance_variable_set( "@" + name, val)
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
create_method( name.to_sym ) {
|
190
|
+
instance_variable_get( "@" + name )
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: abiquo-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marc Cirauqui
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: excon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.43'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.43.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.43'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.43.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: formatador
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.2'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.2.5
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.2'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.2.5
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: json
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.8'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.8.0
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.8'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.8.0
|
73
|
+
description: Simple Abiquo API client
|
74
|
+
email:
|
75
|
+
- marc.cirauqui@abiquo.com
|
76
|
+
executables: []
|
77
|
+
extensions: []
|
78
|
+
extra_rdoc_files: []
|
79
|
+
files:
|
80
|
+
- ".gitignore"
|
81
|
+
- Gemfile
|
82
|
+
- README.md
|
83
|
+
- abiquo-api.gemspec
|
84
|
+
- lib/abiquo-api.rb
|
85
|
+
- lib/abiquo-api/errors.rb
|
86
|
+
- lib/abiquo-api/httpclient.rb
|
87
|
+
- lib/abiquo-api/link.rb
|
88
|
+
- lib/abiquo-api/model.rb
|
89
|
+
- lib/abiquo-api/version.rb
|
90
|
+
homepage: https://github.com/abiquo/api-ruby
|
91
|
+
licenses: []
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.2.2
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: Simple Abiquo API client
|
113
|
+
test_files: []
|
114
|
+
has_rdoc:
|