abiquo-etk 0.6.0 → 0.6.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.
data/Rakefile CHANGED
@@ -16,7 +16,6 @@ begin
16
16
  gem.add_dependency(%q<term-ansicolor>, [">= 1.0"])
17
17
  gem.add_dependency(%q<mixlib-cli>, [">= 1.2"])
18
18
  gem.add_dependency(%q<iniparse>, [">= 1.1.4"])
19
- gem.add_dependency(%q<abiquo>, [">= 0.1.2"])
20
19
  gem.files.include %w(
21
20
  scripts/*
22
21
  lib/**/*
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.6.1
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_WITHOUT: ""
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'resourceful'
4
+ gem 'nokogiri'
5
+
6
+ group :test do
7
+ gem 'steak'
8
+ gem 'webmock'
9
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Abiquo Holdings
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ =Abiquo API Ruby client
2
+
3
+ Rest client to work with the Abiquo's API.
4
+
5
+ ==Installation
6
+
7
+ gem install abiquo
8
+
9
+ ==Usage
10
+
11
+ The first version of the api just uses basic authentication, so we need to create an instance with our credentials:
12
+
13
+ auth = Abiquo::BasicAuth.new('Abiquo', 'username', 'password')
14
+
15
+ The entry point for the Abiquo's API is an atom document service:
16
+
17
+ api = Abiquo::Resource('http://abiquo.example.com/api', auth)
18
+
19
+ from this point we can access to every resource exposed by the api calling nested methods:
20
+
21
+ datacenters = api.datacenters
22
+ racks = api.datacenters.first.racks
23
+
24
+ or get the values of their elements:
25
+
26
+ datacenter = api.datacenters.first
27
+ datacenter.name # => The name of the datacenter
28
+
29
+ You can find further example into the specs:
30
+
31
+ http://github.com/abiquo/api_ruby_client/tree/master/spec/acceptance
32
+
33
+ Further documentation of the API can be found in our wiki:
34
+
35
+ http://abicloud.org/display/ABI16/Abiquo's+API
36
+
37
+ ==Copyright
38
+
39
+ Copyright (c) 2010 Abiquo Holdings. See LICENSE for details.
@@ -0,0 +1,88 @@
1
+ require "rubygems"
2
+ require "rake/gempackagetask"
3
+ require "rake/rdoctask"
4
+
5
+ require "spec"
6
+ require "spec/rake/spectask"
7
+
8
+ desc "Run acceptance specs"
9
+ Spec::Rake::SpecTask.new("spec:acceptance") do |t|
10
+ t.spec_opts = "--format specdoc --colour".split
11
+ t.spec_files = ["spec/acceptance"]
12
+ end
13
+
14
+ desc "Run unit specs"
15
+ Spec::Rake::SpecTask.new("spec:unit") do |t|
16
+ t.spec_files = ["spec/unit"]
17
+ end
18
+
19
+ desc "Run all specs"
20
+ task :spec => ["spec:unit", "spec:acceptance"]
21
+
22
+ task :default => ["spec"]
23
+
24
+ # This builds the actual gem. For details of what all these options
25
+ # mean, and other ones you can add, check the documentation here:
26
+ #
27
+ # http://rubygems.org/read/chapter/20
28
+ #
29
+ spec = Gem::Specification.new do |s|
30
+
31
+ # Change these as appropriate
32
+ s.name = "abiquo"
33
+ s.version = "0.1.2"
34
+ s.summary = "Abiquo API client"
35
+ s.author = "Abiquo"
36
+ s.email = "support@abiquo.com"
37
+ s.homepage = "http://github.com/abiquo/api_ruby_client"
38
+
39
+ s.has_rdoc = true
40
+ # You should probably have a README of some kind. Change the filename
41
+ # as appropriate
42
+ s.extra_rdoc_files = %w(README)
43
+ s.rdoc_options = %w(--main README)
44
+
45
+ # Add any extra files to include in the gem (like your README)
46
+ s.files = %w() + Dir.glob("{lib/**/*}")
47
+ s.require_paths = ["lib"]
48
+
49
+ # If you want to depend on other gems, add them here, along with any
50
+ # relevant versions
51
+ s.add_dependency("resourceful")
52
+ s.add_dependency("nokogiri")
53
+
54
+ # If your tests use any gems, include them here
55
+ s.add_development_dependency("steak")
56
+ s.add_development_dependency("webmock")
57
+ end
58
+
59
+ # This task actually builds the gem. We also regenerate a static
60
+ # .gemspec file, which is useful if something (i.e. GitHub) will
61
+ # be automatically building a gem for this project. If you're not
62
+ # using GitHub, edit as appropriate.
63
+ #
64
+ # To publish your gem online, install the 'gemcutter' gem; Read more
65
+ # about that here: http://gemcutter.org/pages/gem_docs
66
+ Rake::GemPackageTask.new(spec) do |pkg|
67
+ pkg.gem_spec = spec
68
+ end
69
+
70
+ desc "Build the gemspec file #{spec.name}.gemspec"
71
+ task :gemspec do
72
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
73
+ File.open(file, "w") {|f| f << spec.to_ruby }
74
+ end
75
+
76
+ task :package => :gemspec
77
+
78
+ # Generate documentation
79
+ Rake::RDocTask.new do |rd|
80
+
81
+ rd.rdoc_files.include("lib/**/*.rb", "README.rdoc")
82
+ rd.rdoc_dir = "rdoc"
83
+ end
84
+
85
+ desc 'Clear out RDoc and generated packages'
86
+ task :clean => [:clobber_rdoc, :clobber_package] do
87
+ rm "#{spec.name}.gemspec"
88
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{abiquo}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Abiquo"]
9
+ s.date = %q{2010-07-28}
10
+ s.email = %q{support@abiquo.com}
11
+ s.extra_rdoc_files = ["README.rdoc"]
12
+ s.files = ["lib/to_xml.rb", "lib/core_ext.rb", "lib/abiquo.rb", "README.rdoc"]
13
+ s.homepage = %q{http://wiki.github.com/abiquo/abiquo/}
14
+ s.rdoc_options = ["--main", "README.rdoc"]
15
+ s.require_paths = ["lib"]
16
+ s.rubygems_version = %q{1.3.6}
17
+ s.summary = %q{Abiquo API client}
18
+
19
+ if s.respond_to? :specification_version then
20
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
21
+ s.specification_version = 3
22
+
23
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
24
+ s.add_runtime_dependency(%q<resourceful>, [">= 0"])
25
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
26
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
27
+ s.add_development_dependency(%q<steak>, [">= 0"])
28
+ s.add_development_dependency(%q<webmock>, [">= 0"])
29
+ else
30
+ s.add_dependency(%q<resourceful>, [">= 0"])
31
+ s.add_dependency(%q<nokogiri>, [">= 0"])
32
+ s.add_dependency(%q<activesupport>, [">= 0"])
33
+ s.add_dependency(%q<steak>, [">= 0"])
34
+ s.add_dependency(%q<webmock>, [">= 0"])
35
+ end
36
+ else
37
+ s.add_dependency(%q<resourceful>, [">= 0"])
38
+ s.add_dependency(%q<nokogiri>, [">= 0"])
39
+ s.add_dependency(%q<activesupport>, [">= 0"])
40
+ s.add_dependency(%q<steak>, [">= 0"])
41
+ s.add_dependency(%q<webmock>, [">= 0"])
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ gem 'activesupport', '2.3.8'
3
+ require 'abiquo'
4
+ require 'pp'
5
+
6
+ auth = Abiquo::BasicAuth.new('Abiquo', 'admin', 'xabiquo')
7
+ api = Abiquo::Resource('http://as-testing:8080/api', auth)
8
+
9
+ #
10
+ # Create a new Rack
11
+ #
12
+ dc = api.datacenters.first
13
+
14
+ # Create the rack
15
+ rack = dc.racks.create :name => 'myrack01'
16
+
17
+ #
18
+ # This is weird
19
+ #
20
+ # Create the machine
21
+ machine = rack.machines.create :name => 'fooxen', :cpu => '2', :description => 'foo', :hd => '100000', :ram => '1024', :virtualSwitch => 'eth2', :state => 'STOPPED'
22
+
23
+ # Create the HV
24
+ hv = machine.hypervisor.create :type => 'XEN_3', :ip => '10.0.0.1', :port => '8889', :ipService => '10.0.0.1'
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'abiquo'
3
+ require 'pp'
4
+
5
+ auth = Abiquo::BasicAuth.new('Abiquo', 'admin', 'xabiquo')
6
+ api = Abiquo::Resource('http://10.60.1.65/api', auth)
7
+
8
+
9
+ #
10
+ # Create a DataCenter
11
+ #
12
+ #puts "Creating DC1 in BCN..."
13
+ new_datacenter = api.datacenters.create :name => 'DC1', :location => 'BCN'
14
+
15
+ #
16
+ # Create Remote Services
17
+ #
18
+ new_datacenter.remoteServices.create :type => 'VIRTUAL_FACTORY', :uri => 'http://localhost:8080/virtualfactory'
19
+ new_datacenter.remoteServices.create :type => 'STORAGE_SYSTEM_MONITOR', :uri => 'http://localhost:8080/ssm'
20
+ new_datacenter.remoteServices.create :type => 'VIRTUAL_SYSTEM_MONITOR', :uri => 'http://localhost:8080/vsm'
21
+ new_datacenter.remoteServices.create :type => 'NODE_COLLECTOR', :uri => 'http://localhost:8080/nodecollector'
22
+ new_datacenter.remoteServices.create :type => 'APPLIANCE_MANAGER', :uri => 'http://localhost:8080/am'
23
+ new_datacenter.remoteServices.create :type => 'DHCP_SERVICE', :uri => 'http://localhost:7911'
24
+ new_datacenter.remoteServices.create :type => 'BPM_SERVICE', :uri => 'http://localhost:7911'
25
+
26
+ #
27
+ # Create a new Rack
28
+ #
29
+ puts "Creating rack 'myrack01'..."
30
+ new_datacenter.racks.create :name => 'myrack01'
31
+
32
+ #
33
+ # Iterate over all the racks and print the rack name
34
+ #
35
+ puts "Listing racks.."
36
+ new_datacenter.racks.each do |rack|
37
+ pp rack.name
38
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'abiquo'
3
+ require 'pp'
4
+
5
+ auth = Abiquo::BasicAuth.new('Abiquo', 'admin', 'admin')
6
+ api = Abiquo::Resource('http://as-testing.bcn.abiquo.com:8080/api', auth)
7
+
8
+ #
9
+ # Iterate over all the racks and print the rack name and the hypervisors underneath
10
+ #
11
+ api.datacenters.first.racks.each do |rack|
12
+ puts "- RACK [#{rack.name}]"
13
+ rack.machines.each do |m|
14
+ puts "---" + m.name
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'abiquo'
3
+ require 'pp'
4
+
5
+ auth = Abiquo::BasicAuth.new('Abiquo', 'admin', 'admin')
6
+ api = Abiquo::Resource('http://as-testing.bcn.abiquo.com:8080/api', auth)
7
+
8
+ #
9
+ # Create a new Rack
10
+ #
11
+ puts "Creating rack 'myrack01'..."
12
+ api.datacenters.first.racks.create :name => 'myrack01'
13
+
14
+ #
15
+ # Iterate over all the racks and print the rack name
16
+ #
17
+ puts "Listing racks.."
18
+ api.datacenters.first.racks.each do |rack|
19
+ pp rack.name
20
+ end
21
+
22
+ #
23
+ # Iterate over all the racks and print the rack name
24
+ #
25
+ puts "Listing racks..."
26
+ api.datacenters.first.racks.each do |rack|
27
+ pp rack.name
28
+ end
29
+
30
+ #
31
+ # Delete the rack myrack01
32
+ #
33
+ # NOT SUPPORTED IN ABIQUO1.6
34
+ #
35
+ #puts "Deleting rack 'myrack01'..."
36
+ #api.datacenters.first.racks.each do |r|
37
+ # r.delete if r.name == 'myrack01'
38
+ #end
@@ -0,0 +1,210 @@
1
+ require 'resourceful'
2
+ require 'nokogiri'
3
+ require File.expand_path('../active_support/inflections', __FILE__)
4
+ require File.expand_path('../core_ext', __FILE__)
5
+ require File.expand_path('../to_xml', __FILE__)
6
+ require 'uri'
7
+
8
+ module Abiquo
9
+
10
+ class NotAllowed < RuntimeError
11
+ def initialize(method, url)
12
+ @method = method
13
+ @url = url
14
+ end
15
+
16
+ def message
17
+ "Method #{@method} not allowed for #{@url}"
18
+ end
19
+ end
20
+
21
+ class BasicAuth < Resourceful::BasicAuthenticator
22
+ def can_handle?(request); true; end
23
+ end
24
+
25
+ module HttpAccessor
26
+
27
+ def xml
28
+ @xml ||= begin
29
+ response = http.resource(url_with_params).get
30
+ doc = Nokogiri.parse(response.body)
31
+ # HACK: collections have link and totalSize entities we
32
+ # don't want here
33
+ if not doc.search('/*/totalSize').empty?
34
+ doc.search('/*/totalSize | /*/link').each do |node|
35
+ node.remove
36
+ end
37
+ end
38
+ doc.root
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def url_with_params
45
+ @options[:expand] = @options[:expand].join(",") if @options[:expand].is_a?(Array)
46
+ params = @options.map { |k, v| "#{k}=#{v}" }.join("&")
47
+ params = "?#{params}" unless params.empty?
48
+ @url + params
49
+ end
50
+
51
+ def http
52
+ Resourceful::HttpAccessor.new(:authenticator => @auth)
53
+ end
54
+
55
+ def rest_options
56
+ @rest_options ||= begin
57
+ response = http.resource(@url).options
58
+ response.header['Allow'].map {|m| m.to_sym }
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ class SingleResource
65
+
66
+ include HttpAccessor
67
+
68
+ undef id
69
+
70
+ def initialize(url, auth, xml = nil, options = {})
71
+ @url = url
72
+ @auth = auth
73
+ @xml = xml
74
+ @options = options
75
+ end
76
+
77
+ def update(attrs = {})
78
+ raise Abiquo::NotAllowed.new(:PUT, url) unless rest_options.include?(:PUT)
79
+ response = http.resource(url).put(attrs.to_xml(:root => object_type), :content_type => "application/xml")
80
+ @xml = Nokogiri.parse(response.body).root
81
+ end
82
+
83
+ def delete
84
+ raise Abiquo::NotAllowed.new(:DELETE, url) unless rest_options.include?(:DELETE)
85
+ http.resource(url).delete
86
+ end
87
+
88
+ def url
89
+ @url ||= xml.at_xpath("./link[@rel='edit']").try(:[], "href")
90
+ end
91
+
92
+ delegate :to_xml, :to => :xml
93
+ alias_method :inspect, :to_xml
94
+
95
+ private
96
+
97
+ def object_type
98
+ @object_type ||= URI.parse(url).path.split("/")[-2].singularize
99
+ end
100
+
101
+ def method_missing(meth, *args, &blk)
102
+ attribute = xml.at_xpath("./*[name()='#{meth}' or name()='#{attribute_name(meth)}']")
103
+ return node_text(attribute) if attribute
104
+
105
+ link = xml.at_xpath("./link[@rel='#{meth}' or @rel='#{attribute_name(meth)}']")
106
+ return Resource.new(link["href"], @auth, link.at_xpath("./*"), *args) if link
107
+
108
+ if xml.namespaces.has_key?('xmlns:ns2')
109
+ @xml = Nokogiri.parse(xml.to_s.downcase).root
110
+ link = xml.at_xpath("//ns2:collection/xmlns:title[. = '#{meth}']").try(:parent)
111
+ return Resource.new(link['href'], @auth, nil, *args) if link
112
+ end
113
+
114
+ super
115
+ end
116
+
117
+ def node_text(node)
118
+ node.text
119
+ end
120
+
121
+ def attribute_name(attribute)
122
+ attribute.to_s.gsub('_', '-')
123
+ end
124
+
125
+ end
126
+
127
+ class ResourceCollection
128
+
129
+ include HttpAccessor
130
+
131
+ delegate :inspect, :to => :resources
132
+
133
+ def initialize(url, auth, xml = nil, options = {})
134
+ @url = url
135
+ @auth = auth
136
+ @xml = xml if options.empty?
137
+ @options = options
138
+ end
139
+
140
+ def create(attrs = {})
141
+ raise Abiquo::NotAllowed.new(:POST, url) unless rest_options.include?(:POST)
142
+ response = http.resource(url_with_params).post(attrs.to_xml(:root => object_type, :convert_links => true), :content_type => "application/xml")
143
+ doc = Nokogiri.parse(response.body)
144
+ Resource.new(nil, @auth, doc.root)
145
+ end
146
+
147
+ private
148
+
149
+ def object_type
150
+ @object_type ||= URI.parse(@url).path.split("/").last.singularize
151
+ end
152
+
153
+ def resources
154
+ @resources ||= xml.xpath("./*").map { |subnode| Resource.new(subnode.at_xpath("./link[@rel='edit']").try(:[], "href"), @auth, subnode, @options) }
155
+ end
156
+
157
+ def method_missing(meth, *args, &blk)
158
+ resources.send(meth, *args, &blk)
159
+ end
160
+ end
161
+
162
+ class Resource
163
+
164
+ include HttpAccessor
165
+
166
+ undef id
167
+ undef type
168
+
169
+ delegate :inspect, :to => :get!
170
+
171
+ def self.from_xml(xml, auth = nil)
172
+ doc = Nokogiri.parse(xml)
173
+ new(nil, auth, doc.root)
174
+ end
175
+
176
+ def initialize(url, auth, xml = nil, options = {})
177
+ @url = url
178
+ @auth = auth
179
+ @xml = xml
180
+ @options = options
181
+ end
182
+
183
+ def method_missing(meth, *args, &blk)
184
+ @resource_object ||= resource_class(meth).new(@url, @auth, @xml, @options)
185
+ @resource_object.send(meth, *args, &blk)
186
+ end
187
+
188
+ def resource_class(meth)
189
+ @resource_class ||= if (Array.instance_methods + ["create"] - ["delete", "id"]).include?(meth.to_s)
190
+ ResourceCollection
191
+ else
192
+ SingleResource
193
+ end
194
+ end
195
+
196
+ def get!
197
+ klass = SingleResource
198
+ if (!xml.children.empty? && xml.name.singularize == xml.children.first.name)
199
+ klass = ResourceCollection
200
+ end
201
+ @resource_object = klass.new(@url, @auth, @xml, @options)
202
+ end
203
+
204
+ end
205
+
206
+ def self.Resource(url, auth, params = {})
207
+ Resource.new(url, auth, nil, params)
208
+ end
209
+
210
+ end