abiquo-etk 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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