This is the interface to the factory agent, the service,
(which is what conductor will communicate with) will be in
an upcoming patch.
---
services/image_factory/README | 44 ++++++
services/image_factory/console/Rakefile | 67 +++++++++
.../image_factory/console/lib/image_factory.rb | 2 +
.../console/lib/image_factory/base_handler.rb | 56 ++++++++
.../lib/image_factory/image_factory_console.rb | 141 ++++++++++++++++++++
.../console/spec/image_factory/console_spec.rb | 92 +++++++++++++
services/image_factory/console/spec/spec_helper.rb | 3 +
7 files changed, 405 insertions(+), 0 deletions(-)
create mode 100644 services/image_factory/README
create mode 100644 services/image_factory/console/Rakefile
create mode 100644 services/image_factory/console/lib/image_factory.rb
create mode 100644 services/image_factory/console/lib/image_factory/base_handler.rb
create mode 100644
services/image_factory/console/lib/image_factory/image_factory_console.rb
create mode 100644 services/image_factory/console/spec/image_factory/console_spec.rb
create mode 100644 services/image_factory/console/spec/spec_helper.rb
diff --git a/services/image_factory/README b/services/image_factory/README
new file mode 100644
index 0000000..7c70d9b
--- /dev/null
+++ b/services/image_factory/README
@@ -0,0 +1,44 @@
+First, you'll need:
+* rebuild the rpms for new qpid/qmf:
+
http://koji.fedoraproject.org/koji/packageinfo?packageID=10015
+* build rpm for oz (
http://www.aeolusproject.org/oz-download.html)
+* You can run imageFactory from src, or build the rpm, source is here:
+
git://github.com/aeolusproject/image_factory
+
+I have been developing for now using the following packages for qpid:
+qpid-cpp-server-0.8-6.fc14.x86_64
+python-qpid-qmf-0.8-6.fc14.x86_64
+ruby-qpid-qmf-0.8-6.fc14.x86_64
+qpid-tools-0.8-6.fc14.noarch
+qpid-qmf-0.8-6.fc14.x86_64
+qpid-cpp-client-0.8-6.fc14.x86_64
+qpid-cpp-server-store-0.8-6.fc14.x86_64
+
+Note that you will need to rpm -e --nodeps all older qpid stuff
+if you dont want to uninstall the entire conductor stack.
+
+Make sure /etc/qpidd.conf has the line:
+auth=no
+and restart qpidd service
+
+In the console dir,
+run tests with:
+[console]$ rake spec
+
+Build the console gem and install:
+[console]$ rake gem
+[console]$ sudo gem install pkg/image_factory_console-0.2.0.gem
+
+You can start the factory (from src) with:
+[factory-checkout]$ sudo ./imgfac.py --qmf --foreground
+
+In the image_factory_connector/bin dir, run the app in any of the following ways:
+* [image_factory_connector/bin]$ ./image_factory_connector -p 2003 start
+* [image_factory_connector/lib]$ thin -R config.ru -p 2003 start
+* [image_factory_connector/lib]$ rackup -p 2003 config.ru
+
+View some simple interactions (defined in image_factory_connector) from browser with:
+http://localhost:2003/
+http://localhost:2003/do-something
+http://localhost:2003/shutdown
+
diff --git a/services/image_factory/console/Rakefile
b/services/image_factory/console/Rakefile
new file mode 100644
index 0000000..fa07cb2
--- /dev/null
+++ b/services/image_factory/console/Rakefile
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2011 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt(a)redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+
+require 'rubygems'
+require 'rake'
+require 'rake/clean'
+require 'rake/gempackagetask'
+require 'rake/rdoctask'
+require 'rake/testtask'
+require 'spec/rake/spectask'
+
+spec = Gem::Specification.new do |s|
+ s.name = 'image_factory_console'
+ s.version = '0.2.0'
+ s.has_rdoc = true
+ #s.extra_rdoc_files = ['README', 'COPYING']
+ s.summary = 'QMF Console for Aeolus Image Factory'
+ s.description = s.summary
+ s.author = 'Jason Guiditta'
+ s.email = 'jguiditt(a)redhat.com'
+ # s.executables = ['your_executable_here']
+ s.files = %w(Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
+ s.require_path = "lib"
+ s.bindir = "bin"
+end
+
+Rake::GemPackageTask.new(spec) do |p|
+ p.gem_spec = spec
+ p.need_tar = true
+ p.need_zip = true
+end
+
+Rake::RDocTask.new do |rdoc|
+ files =['lib/**/*.rb'] #'README', 'COPYING',
+ rdoc.rdoc_files.add(files)
+ #rdoc.main = "README" # page to start on
+ rdoc.title = "console Docs"
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
+ rdoc.options << '--line-numbers'
+end
+
+Rake::TestTask.new do |t|
+ t.test_files = FileList['test/**/*.rb']
+end
+
+Spec::Rake::SpecTask.new do |t|
+ t.libs << 'lib'
+ t.spec_files = FileList['spec/**/*.rb']
+ t.spec_opts = ['--color', '--format nested']
+end
diff --git a/services/image_factory/console/lib/image_factory.rb
b/services/image_factory/console/lib/image_factory.rb
new file mode 100644
index 0000000..af8f3f9
--- /dev/null
+++ b/services/image_factory/console/lib/image_factory.rb
@@ -0,0 +1,2 @@
+require 'image_factory/image_factory_console'
+require 'image_factory/base_handler'
\ No newline at end of file
diff --git a/services/image_factory/console/lib/image_factory/base_handler.rb
b/services/image_factory/console/lib/image_factory/base_handler.rb
new file mode 100644
index 0000000..7dcead8
--- /dev/null
+++ b/services/image_factory/console/lib/image_factory/base_handler.rb
@@ -0,0 +1,56 @@
+#
+# Copyright (C) 2011 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt(a)redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+require 'logger'
+
+class BaseHandler
+
+ def initialize(logger=nil)
+ logger(logger)
+ end
+
+ def handle(data)
+ logger.debug "====== Type of event: #{data.event}"
+ #puts "should be calling the logger now..."
+ if data.event == 'STATUS'
+ handle_status(data)
+ elsif data.event == 'FAILURE'
+ handle_failed(data)
+ end
+ end
+
+ def handle_status(data)
+ logger.debug "{data.event}, #{data.new_status}"
+ end
+
+ def handle_failed(data)
+ logger.error "#{data.to_s}"
+ end
+
+ private
+ def logger(logger=nil)
+ @logger ||= logger
+ unless @logger
+ @logger = Logger.new(STDOUT)
+ @logger.level = Logger::ERROR
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+ end
+ return @logger
+ end
+end
diff --git a/services/image_factory/console/lib/image_factory/image_factory_console.rb
b/services/image_factory/console/lib/image_factory/image_factory_console.rb
new file mode 100644
index 0000000..6f9e11b
--- /dev/null
+++ b/services/image_factory/console/lib/image_factory/image_factory_console.rb
@@ -0,0 +1,141 @@
+#
+# Copyright (C) 2011 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt(a)redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+# TODO: figure out what I am doing wrong here that I need this line
+$: << File.expand_path(File.join(File.dirname(__FILE__), "."))
+require 'cqpid'
+require 'qmf2'
+require 'logger'
+require 'base_handler'
+
+class ImageFactoryConsole < Qmf2::ConsoleHandler
+
+ attr_accessor :q, :handler
+
+ def initialize(args={})
+# @retry_limit = args.include?(:retry_limit) ? args[:retry_limit] : 20
+# @delay = args.include?(:delay) ? args[:delay] : 15
+ host = args.include?(:host) ? args[:host] : "localhost"
+ port = args.include?(:port) ? args[:port] : 5672
+# url = "amqp://#{host}:#{port}" <- the amqp part here doesnt work yet
+ url = "#{host}:#{port}"
+ conn_options = args.include?(:retry_limit)?
"reconnect-limit:#{args[:retry_limit]}" : ""
+ @connection = Cqpid::Connection.new(url, conn_options)
+ @connection.open
+ @session = Qmf2::ConsoleSession.new(@connection)
+ @session.open
+ @session.set_agent_filter("[and, [eq, _vendor, [quote, 'redhat.com']],
[eq, _product, [quote, 'imagefactory']]]")
+
+ if args.include?(:logger)
+ @logger = args[:logger]
+ else
+ @logger = Logger.new(STDOUT)
+ @logger.level = Logger::ERROR
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+ end
+ @handler = args.include?(:handler)? args[:handler]: BaseHandler.new
+ super(@session)
+ end
+
+ # Call this method to initiate a build, and get back an
+ # BuildAdaptor object.
+ # * descriptor => String
+ # This can be either xml or a url pointing to the xml
+ # * target => String
+ # Represents the target provider type to build for (ec2, mock, etc)
+ # * Returns => a BuildAdaptor object
+ #
+ def build_image(descriptor, target)
+ # TODO: return error if there is a problem calling this method or getting
+ # a factory instance
+ response = factory.image(descriptor, target)
+ build_adaptor(response)
+ end
+
+ # Call this method to push an image to a provider, and get back an
+ # BuildAdaptor object.
+ # * image_id => String (uuid)
+ # * provider => String
+ # Represents the target provider to build for (ec2-us-east, mock, etc)
+ # * credentials => String
+ # XML block to be used for registration, upload, etc
+ # * Returns => a BuildAdaptor object
+ #
+ def push_image(image_id, provider, credentials)
+ # TODO: return error if there is a problem calling this method or getting
+ # a factory instance
+ response = factory.provider_image(image_id, provider, credentials)
+ build_adaptor(response)
+ end
+
+ #TODO: enhance both of these methods to handle multiple agents
+ def agent_added(agent)
+ @logger.debug "GOT AN AGENT: #{agent} at #{Time.now.utc}"
+ @q = agent if agent.product == "imagefactory"
+ end
+
+ def agent_deleted(agent, reason)
+ @logger.debug "AGENT GONE: #{agent} at #{Time.now.utc}, because
#{reason}"
+ unless @q==nil
+ @q = {} if @q.product == agent.product
+ end
+ end
+
+ # TODO: handle agent restart events (this will be more useful when
+ # restarted agent can recover an in-process build
+ def agent_restarted(agent)
+ @logger.debug "AGENT RESTARTED: #{agent.product}"
+ end
+
+ # TODO: handle schema updates. This will be more useful when/if
+ # we make this a more generic console to talk to different agents.
+ def agent_schema_updated(agent)
+ @logger.debug "AGENT SCHEMA UPDATED: #{agent.product}"
+ end
+
+ def event_raised(agent, data, timestamp, severity)
+ @logger.debug "GOT AN EVENT: #{agent}, #{data} at #{timestamp}"
+ @handler.handle(data)
+ end
+
+ def shutdown
+ @logger.debug "Closing connections.."
+ if @session
+ @session.close
+ end
+ @connection.close
+ self.cancel
+ end
+
+ private
+
+ def factory
+ @factory ||= @q.query("{class:ImageFactory,
package:'com.redhat.imagefactory'}").first
+ end
+
+ def build_adaptor(response)
+ imgfacaddr = Qmf2::DataAddr.new(response['build_adaptor'])
+ query = Qmf2::Query.new(imgfacaddr)
+ @q.query(query).first
+ end
+
+end
+
+#i = ImageBuilderConsole.new
+#i.run
diff --git a/services/image_factory/console/spec/image_factory/console_spec.rb
b/services/image_factory/console/spec/image_factory/console_spec.rb
new file mode 100644
index 0000000..c7d5a69
--- /dev/null
+++ b/services/image_factory/console/spec/image_factory/console_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+module ImageFactory
+ describe ImageFactoryConsole do
+ context "needs a real agent running" do
+ # for now, these all require an actual agent on the bus
+ before(:all) do
+ @logger = Logger.new(STDOUT)
+ @logger.level = Logger::DEBUG
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+ @i = ImageFactoryConsole.new()
+ @i.start
+ # Sadly, this is needed so we wait for an agent to appear.
+ # We might be able to extract this somewhere to hold off
+ # on tests until we get an agent_added notification
+ sleep(5)
+ end
+
+ describe "#q" do
+ it "should get an agent set" do
+ @i.q.should_not be_nil
+ end
+ end
+
+ describe "#build_image" do
+ it "should return a build-adaptor with uuid" do
+ @i.build_image("<template></template>",
"mock").image_id.should_not be_nil
+ end
+ end
+
+ describe "#push_image" do
+ it "should return a build-adaptor with uuid" do
+ @image = @i.build_image("<template></template>",
"mock")
+ #(a)i.handler = BaseHandler.new(@logger)
+ @i.handler.should_not_receive(:handle_failed)
+ @i.push_image((a)image.image_id, "mock", "some
creds").image_id.should_not be_nil
+ end
+ end
+
+ after(:all) do
+ @i.shutdown
+ end
+ end
+
+ context "agent is stubbed" do
+ before(:each) do
+ @agent = mock('agent', :null_object => true)
+ @agent.stub(:product).and_return("imagefactory")
+ @output = double('output')
+ @i2 = ImageFactoryConsole.new(:logger => @output)
+ end
+
+ describe "#agent_added" do
+ it "logs when a new agent appears" do
+ @output.should_receive(:debug).with(/GOT AN AGENT/).as_null_object
+ @i2.agent_added(@agent)
+ end
+
+ it "stores a reference to that agent if it is a factory" do
+ @output.should_receive(:debug).with(/GOT AN AGENT/)
+ @i2.agent_added(@agent)
+ @i2.q.should respond_to(:product)
+ end
+ end
+
+ describe "#event_raised" do
+ before(:each) do
+ @data = mock('data', :null_object => true)
+ @data.stub(:event).and_return("STATUS")
+ end
+
+ it "should log an event" do
+ @output.should_receive(:debug).with(/GOT AN EVENT/)
+ @i2.event_raised(@agent, @data, Time.now, "horrid")
+ end
+
+ it "should call handle on an event" do
+ @output.should_receive(:debug).with(/GOT AN EVENT/)
+
+ @i2.handler.should_receive(:handle).once
+ @i2.event_raised(@agent, @data, Time.now, "horrid")
+ end
+ end
+
+ after(:each) do
+ @output.should_receive(:debug).with(/Closing/)
+ @i2.shutdown
+ end
+ end
+
+ end
+end
diff --git a/services/image_factory/console/spec/spec_helper.rb
b/services/image_factory/console/spec/spec_helper.rb
new file mode 100644
index 0000000..56baf4a
--- /dev/null
+++ b/services/image_factory/console/spec/spec_helper.rb
@@ -0,0 +1,3 @@
+$: << File.expand_path(File.join(File.dirname(__FILE__), "../lib"))
+require 'rubygems'
+require 'image_factory'
--
1.7.4