ACK
On Jun 15, 2011, at 6:45 AM, Mark McLoughlin wrote:
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