A job is essentially what we're modelling here - e.g. it has status and
percent_complete properties.
---
imagefactory/BuildDispatcher.py | 105 ++++----------------------------
imagefactory/BuildJob.py | 106 +++++++++++++++++++++++++++++++++
imagefactory/qmfagent/BuildAdaptor.py | 4 +-
imagefactory/qmfagent/ImageFactory.py | 5 +-
tests/testBuildDispatcher.py | 41 -------------
tests/testBuildJob.py | 41 +++++++++++++
6 files changed, 165 insertions(+), 137 deletions(-)
create mode 100644 imagefactory/BuildJob.py
delete mode 100644 tests/testBuildDispatcher.py
create mode 100644 tests/testBuildJob.py
diff --git a/imagefactory/BuildDispatcher.py b/imagefactory/BuildDispatcher.py
index 4bae177..a1cfc9d 100644
--- a/imagefactory/BuildDispatcher.py
+++ b/imagefactory/BuildDispatcher.py
@@ -15,15 +15,10 @@
# MA 02110-1301, USA. A copy of the GNU General Public License is
# also available at
http://www.gnu.org/copyleft/gpl.html.
-import sys
import libxml2
-import logging
import os.path
-import props
-from threading import Thread, Lock
-from imagefactory.builders import *
from imagefactory.ApplicationConfiguration import ApplicationConfiguration
-from imagefactory.ImageFactoryException import ImageFactoryException
+from imagefactory.BuildJob import BuildJob
from imagefactory.ImageWarehouse import ImageWarehouse
from imagefactory.Template import Template
@@ -35,82 +30,8 @@ from imagefactory.Template import Template
class BuildDispatcher(object):
- template = props.prop("_template", "The template property.")
- target = props.prop("_target", "The target property.")
- image_id = props.prop("_image_id", "The UUID of the image.")
- build_id = props.prop("_build_id", "The UUID of the build.")
- provider = props.prop("_provider", "The provider being pushed
to.")
- status = props.prop("_status", "The status property.")
- percent_complete = props.prop("_percent_complete", "The
percent_complete property.")
- new_image_id = props.prop("_new_image_id" "The image property.")
-
- def __init__(self, template, target, image_id = '', build_id = ''):
- super(BuildDispatcher, self).__init__()
-
- self.log = logging.getLogger('%s.%s' % (__name__,
self.__class__.__name__))
-
- self.template = template if isinstance(template, Template) else
Template(template)
- self.target = target
- self.image_id = image_id
- self.build_id = build_id
- self.provider = None
- self.status = "New"
- self.percent_complete = 0
- self._watcher = None
-
- self._builder = self._get_builder()
- self._builder.delegate = self
-
- self.new_image_id = self._builder.new_image_id
-
- def build_image(self, watcher=None):
- self._watcher = watcher
- kwargs = dict(build_id=self.build_id)
- self._start_builder_thread("build_image", arg_dict=kwargs)
-
- def push_image(self, target_image_id, provider, credentials, watcher=None):
- self._watcher = watcher
- self.provider = provider
- kwargs = dict(target_image_id=target_image_id, provider=provider,
credentials=credentials)
- self._start_builder_thread("push_image", arg_dict=kwargs)
-
- def abort(self):
- self._builder.abort()
-
- def builder_did_update_status(self, builder, old_status, new_status):
- self.status = new_status
- if self.status == "COMPLETED" and self._watcher:
- self._watcher.completed()
- self._watcher = None
-
- def builder_did_update_percentage(self, builder, original_percentage,
new_percentage):
- self.percent_complete = new_percentage
-
- def builder_did_fail(self, builder, failure_type, failure_info):
- pass
-
- def _get_builder(self):
- builder_class = MockBuilder.MockBuilder
- if (self.target != "mock"): # If target is mock always run mock builder
regardless of template
- os_name = self.__class__._xml_node(self.template.xml,
'/template/os/name')
- class_name = "%sBuilder" % (os_name, )
- try:
- module_name = "imagefactory.builders.%s" % (class_name, )
- __import__(module_name)
- builder_class = getattr(sys.modules[module_name], class_name)
- except AttributeError, e:
- self.log.exception("CAUGHT EXCEPTION: %s \n Could not find builder
class for %s, returning MockBuilder!", e, os_name)
-
- return builder_class(self.template, self.target)
-
- def _start_builder_thread(self, method_name, arg_dict={}):
- thread_name = "%s.%s()" % (self.new_image_id, method_name)
- # using args to pass the method we want to call on the target object.
- builder_thread = Thread(target = self._builder, name=thread_name,
args=(method_name), kwargs=arg_dict)
- builder_thread.start()
-
@classmethod
- def build_image_for_targets(cls, image_id, build_id, template, targets, *args,
**kwargs):
+ def build_image_for_targets(cls, image_id, build_id, template, targets, job_cls =
BuildJob, *args, **kwargs):
warehouse = cls._get_warehouse()
template = Template(template)
@@ -120,16 +41,16 @@ class BuildDispatcher(object):
watcher = BuildWatcher(image_id, build_id, len(targets), warehouse)
- dispatchers = []
+ jobs = []
for target in targets:
- dispatcher = cls(template, target, image_id, build_id, *args, **kwargs)
- dispatcher.build_image(watcher)
- dispatchers.append(dispatcher)
+ job = job_cls(template, target, image_id, build_id, *args, **kwargs)
+ job.build_image(watcher)
+ jobs.append(job)
- return dispatchers
+ return jobs
@classmethod
- def push_image_to_providers(cls, image_id, build_id, providers, credentials, *args,
**kwargs):
+ def push_image_to_providers(cls, image_id, build_id, providers, credentials, job_cls
= BuildJob, *args, **kwargs):
warehouse = cls._get_warehouse()
if not build_id:
@@ -137,7 +58,7 @@ class BuildDispatcher(object):
watcher = PushWatcher(image_id, build_id, len(providers), warehouse)
- dispatchers = []
+ jobs = []
for provider in providers:
target = cls._map_provider_to_target(provider)
@@ -145,11 +66,11 @@ class BuildDispatcher(object):
template = cls._template_for_target_image_id(warehouse, target_image_id)
- dispatcher = cls(template, target, image_id, build_id, *args, **kwargs)
- dispatcher.push_image(target_image_id, provider, credentials, watcher)
- dispatchers.append(dispatcher)
+ job = job_cls(template, target, image_id, build_id, *args, **kwargs)
+ job.push_image(target_image_id, provider, credentials, watcher)
+ jobs.append(job)
- return dispatchers
+ return jobs
@classmethod
def _get_warehouse(cls):
diff --git a/imagefactory/BuildJob.py b/imagefactory/BuildJob.py
new file mode 100644
index 0000000..a5db89a
--- /dev/null
+++ b/imagefactory/BuildJob.py
@@ -0,0 +1,106 @@
+# Copyright (C) 2010-2011 Red Hat, Inc.
+#
+# 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.
+
+import libxml2
+import logging
+import props
+import sys
+import threading
+from imagefactory.builders import *
+from imagefactory.Template import Template
+
+class BuildJob(object):
+
+ template = props.prop("_template", "The template property.")
+ target = props.prop("_target", "The target property.")
+ image_id = props.prop("_image_id", "The UUID of the image.")
+ build_id = props.prop("_build_id", "The UUID of the build.")
+ provider = props.prop("_provider", "The provider being pushed
to.")
+ status = props.prop("_status", "The status property.")
+ percent_complete = props.prop("_percent_complete", "The
percent_complete property.")
+ new_image_id = props.prop("_new_image_id" "The image property.")
+
+ def __init__(self, template, target, image_id = '', build_id = ''):
+ super(BuildJob, self).__init__()
+
+ self.log = logging.getLogger('%s.%s' % (__name__,
self.__class__.__name__))
+
+ self.template = template if isinstance(template, Template) else
Template(template)
+ self.target = target
+ self.image_id = image_id
+ self.build_id = build_id
+ self.provider = None
+ self.status = "New"
+ self.percent_complete = 0
+ self._watcher = None
+
+ self._builder = self._get_builder()
+ self._builder.delegate = self
+
+ self.new_image_id = self._builder.new_image_id
+
+ def build_image(self, watcher=None):
+ self._watcher = watcher
+ kwargs = dict(build_id=self.build_id)
+ self._start_builder_thread("build_image", arg_dict=kwargs)
+
+ def push_image(self, target_image_id, provider, credentials, watcher=None):
+ self._watcher = watcher
+ self.provider = provider
+ kwargs = dict(target_image_id=target_image_id, provider=provider,
credentials=credentials)
+ self._start_builder_thread("push_image", arg_dict=kwargs)
+
+ def abort(self):
+ self._builder.abort()
+
+ def builder_did_update_status(self, builder, old_status, new_status):
+ self.status = new_status
+ if self.status == "COMPLETED" and self._watcher:
+ self._watcher.completed()
+ self._watcher = None
+
+ def builder_did_update_percentage(self, builder, original_percentage,
new_percentage):
+ self.percent_complete = new_percentage
+
+ def builder_did_fail(self, builder, failure_type, failure_info):
+ pass
+
+ def _get_builder(self):
+ builder_class = MockBuilder.MockBuilder
+ if (self.target != "mock"): # If target is mock always run mock builder
regardless of template
+ os_name = self._xml_node(self.template.xml, '/template/os/name')
+ class_name = "%sBuilder" % (os_name, )
+ try:
+ module_name = "imagefactory.builders.%s" % (class_name, )
+ __import__(module_name)
+ builder_class = getattr(sys.modules[module_name], class_name)
+ except AttributeError, e:
+ self.log.exception("CAUGHT EXCEPTION: %s \n Could not find builder
class for %s, returning MockBuilder!", e, os_name)
+
+ return builder_class(self.template, self.target)
+
+ def _start_builder_thread(self, method_name, arg_dict={}):
+ thread_name = "%s.%s()" % (self.new_image_id, method_name)
+ # using args to pass the method we want to call on the target object.
+ builder_thread = threading.Thread(target = self._builder, name=thread_name,
args=(method_name), kwargs=arg_dict)
+ builder_thread.start()
+
+ def _xml_node(self, xml, xpath):
+ nodes = libxml2.parseDoc(xml).xpathEval(xpath)
+ if not nodes:
+ return None
+ return nodes[0].content
diff --git a/imagefactory/qmfagent/BuildAdaptor.py
b/imagefactory/qmfagent/BuildAdaptor.py
index 78b0570..e042759 100644
--- a/imagefactory/qmfagent/BuildAdaptor.py
+++ b/imagefactory/qmfagent/BuildAdaptor.py
@@ -23,9 +23,9 @@ from qmf2 import *
from threading import Thread, Lock
from imagefactory import props
from imagefactory.builders import *
-from imagefactory.BuildDispatcher import BuildDispatcher
+from imagefactory.BuildJob import BuildJob
-class BuildAdaptor(BuildDispatcher):
+class BuildAdaptor(BuildJob):
# QMF schema for BuildAdaptor
qmf_schema = Schema(SCHEMA_TYPE_DATA, "com.redhat.imagefactory",
"BuildAdaptor")
qmf_schema.addProperty(SchemaProperty("image", SCHEMA_DATA_STRING,
desc="UUID of the image"))
diff --git a/imagefactory/qmfagent/ImageFactory.py
b/imagefactory/qmfagent/ImageFactory.py
index c5e64fa..460883c 100644
--- a/imagefactory/qmfagent/ImageFactory.py
+++ b/imagefactory/qmfagent/ImageFactory.py
@@ -23,6 +23,7 @@ import httplib2
from BuildAdaptor import BuildAdaptor
from imagefactory import props
from imagefactory.ApplicationConfiguration import ApplicationConfiguration
+from imagefactory.BuildDispatcher import BuildDispatcher
from imagefactory.ImageWarehouse import ImageWarehouse
from imagefactory.Singleton import Singleton
from imagefactory.Template import Template
@@ -107,10 +108,10 @@ class ImageFactory(Singleton):
raise RuntimeError("Could not return build_adaptor!\nimage_metadata:
%s\ntemplate_id: %s\ntemplate: %s\n" % (image_metadata, template_id, target))
def build_image(self, image, build, template, targets):
- return BuildAdaptor.build_image_for_targets(image, build, template, targets,
self.agent)
+ return BuildDispatcher.build_image_for_targets(image, build, template, targets,
BuildAdaptor, self.agent)
def push_image(self, image, build, providers, credentials):
- return BuildAdaptor.push_image_to_providers(image, build, providers, credentials,
self.agent)
+ return BuildDispatcher.push_image_to_providers(image, build, providers,
credentials, BuildAdaptor, self.agent)
def instance_states(self, class_name):
"""Returns a dictionary representing the finite state machine for
instances of the class specified."""
diff --git a/tests/testBuildDispatcher.py b/tests/testBuildDispatcher.py
deleted file mode 100644
index 226da12..0000000
--- a/tests/testBuildDispatcher.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-
-# Copyright (C) 2010-2011 Red Hat, Inc.
-#
-# 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.
-
-import unittest
-import logging
-from imagefactory import BuildDispatcher
-from imagefactory.builders import MockBuilder
-
-class testBuildDispatcher(unittest.TestCase):
- def setUp(self):
- logging.basicConfig(level=logging.NOTSET, format='%(asctime)s %(levelname)s
%(name)s pid(%(process)d) Message: %(message)s',
filename='/tmp/imagefactory-unittests.log')
-
- def tearDown(self):
- pass
-
- def testInstantiateMockBuilder(self):
- template_xml =
"<template><name>f14jeos</name><os><name>Fedora</name></os></template>"
- dispatcher = BuildDispatcher.BuildDispatcher(template_xml, "mock")
- self.assertIsInstance(dispatcher._builder, MockBuilder.MockBuilder)
- self.assertEqual(dispatcher.template.xml, template_xml)
- self.assertEqual(dispatcher.target, "mock")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/testBuildJob.py b/tests/testBuildJob.py
new file mode 100644
index 0000000..28c75e6
--- /dev/null
+++ b/tests/testBuildJob.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+# Copyright (C) 2010-2011 Red Hat, Inc.
+#
+# 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.
+
+import unittest
+import logging
+from imagefactory import BuildJob
+from imagefactory.builders import MockBuilder
+
+class testBuildJob(unittest.TestCase):
+ def setUp(self):
+ logging.basicConfig(level=logging.NOTSET, format='%(asctime)s %(levelname)s
%(name)s pid(%(process)d) Message: %(message)s',
filename='/tmp/imagefactory-unittests.log')
+
+ def tearDown(self):
+ pass
+
+ def testInstantiateMockBuilder(self):
+ template_xml =
"<template><name>f14jeos</name><os><name>Fedora</name></os></template>"
+ job = BuildJob.BuildJob(template_xml, "mock")
+ self.assertIsInstance(job._builder, MockBuilder.MockBuilder)
+ self.assertEqual(job.template.xml, template_xml)
+ self.assertEqual(job.target, "mock")
+
+if __name__ == '__main__':
+ unittest.main()
--
1.7.4.4