Hi Steve,
Here's a series of patches to resolve issues with the previous series.
Cheers, Mark.
We were accidentally storing the object type for a target image as "image" --- imagefactory/ImageWarehouse.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/imagefactory/ImageWarehouse.py b/imagefactory/ImageWarehouse.py index 973f7c4..9fbd110 100644 --- a/imagefactory/ImageWarehouse.py +++ b/imagefactory/ImageWarehouse.py @@ -212,7 +212,7 @@ class ImageWarehouse(object): except Exception, e: raise WarehouseError("Problem encountered trying to reach image warehouse. Please check that iwhd is running and reachable.\nException text: %s" % (e, ))
- meta_data = dict(uuid=target_image_id, object_type="image") + meta_data = dict(uuid=target_image_id, object_type="target_image") if(metadata): meta_data.update(metadata) self.set_metadata_for_object_at_url(meta_data, object_url)
ACK
On Jun 15, 2011, at 6:45 AM, Mark McLoughlin wrote:
We were accidentally storing the object type for a target image as "image"
imagefactory/ImageWarehouse.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/imagefactory/ImageWarehouse.py b/imagefactory/ImageWarehouse.py index 973f7c4..9fbd110 100644 --- a/imagefactory/ImageWarehouse.py +++ b/imagefactory/ImageWarehouse.py @@ -212,7 +212,7 @@ class ImageWarehouse(object): except Exception, e: raise WarehouseError("Problem encountered trying to reach image warehouse. Please check that iwhd is running and reachable.\nException text: %s" % (e, ))
meta_data = dict(uuid=target_image_id, object_type="image")
meta_data = dict(uuid=target_image_id, object_type="target_image") if(metadata): meta_data.update(metadata) self.set_metadata_for_object_at_url(meta_data, object_url)
-- 1.7.4.4
And use it for ImageFactoryAgent.managedObjects --- imagefactory/props.py | 25 ++++++++++--------------- imagefactory/qmfagent/ImageFactoryAgent.py | 8 +------- 2 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/imagefactory/props.py b/imagefactory/props.py index cc2616e..b7eadd0 100644 --- a/imagefactory/props.py +++ b/imagefactory/props.py @@ -16,37 +16,32 @@ # also available at http://www.gnu.org/copyleft/gpl.html.
# -# TODO: -# - add support for read-only properties -# - -# -# Return a copy of dict d with the specified keys filtered out -# -def filter_dict(d, keys): - return dict(map(lambda k: [k, d[k]], filter(lambda k: not k in keys, d.keys()))) - -# # Return a property backed by the given attribute # -def prop(attr, doc = None): +def prop(attr, doc = None, ro = None): def fget(self): return getattr(self, attr) def fset(self, value): setattr(self, attr, value) def fdel(self): delattr(self, attr) - return property(**filter_dict(locals().copy(), ['attr'])) + return property(fget, fset if not ro else None, fdel if not ro else None, doc) + +def ro_prop(attr, doc = None): + return prop(attr, doc, True)
# # A variant of the above where the property is backed by an # attribute of an attribute # -def subprop(attr, subattr, doc = None): +def subprop(attr, subattr, doc = None, ro = False): def fget(self): return getattr(getattr(self, attr), subattr) def fset(self, value): setattr(getattr(self, attr), subattr, value) def fdel(self): delattr(getattr(self, attr), subattr) - return property(**filter_dict(locals().copy(), ['attr', 'subattr'])) + return property(fget, fset if not ro else None, fdel if not ro else None, doc) + +def ro_subprop(attr, subattr, doc = None): + return prop(attr, subattr, doc, True) diff --git a/imagefactory/qmfagent/ImageFactoryAgent.py b/imagefactory/qmfagent/ImageFactoryAgent.py index 73cd1db..0017cec 100644 --- a/imagefactory/qmfagent/ImageFactoryAgent.py +++ b/imagefactory/qmfagent/ImageFactoryAgent.py @@ -26,13 +26,7 @@ from imagefactory import props class ImageFactoryAgent(AgentHandler):
qmf_object = props.prop("_qmf_object", "The qmf_object property.") - - def managedObjects(): - doc = "The managedObjects property." - def fget(self): - return self._managedObjects - return locals() - managedObjects = property(**managedObjects()) + managedObjects = props.ro_prop("_managedObjects", "The managedObjects property.")
def __init__(self, url): self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
ACK
On Jun 15, 2011, at 6:45 AM, Mark McLoughlin wrote:
And use it for ImageFactoryAgent.managedObjects
imagefactory/props.py | 25 ++++++++++--------------- imagefactory/qmfagent/ImageFactoryAgent.py | 8 +------- 2 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/imagefactory/props.py b/imagefactory/props.py index cc2616e..b7eadd0 100644 --- a/imagefactory/props.py +++ b/imagefactory/props.py @@ -16,37 +16,32 @@ # also available at http://www.gnu.org/copyleft/gpl.html.
# -# TODO: -# - add support for read-only properties -#
-# -# Return a copy of dict d with the specified keys filtered out -# -def filter_dict(d, keys):
- return dict(map(lambda k: [k, d[k]], filter(lambda k: not k in keys, d.keys())))
-# # Return a property backed by the given attribute # -def prop(attr, doc = None): +def prop(attr, doc = None, ro = None): def fget(self): return getattr(self, attr) def fset(self, value): setattr(self, attr, value) def fdel(self): delattr(self, attr)
- return property(**filter_dict(locals().copy(), ['attr']))
- return property(fget, fset if not ro else None, fdel if not ro else None, doc)
+def ro_prop(attr, doc = None):
- return prop(attr, doc, True)
# # A variant of the above where the property is backed by an # attribute of an attribute # -def subprop(attr, subattr, doc = None): +def subprop(attr, subattr, doc = None, ro = False): def fget(self): return getattr(getattr(self, attr), subattr) def fset(self, value): setattr(getattr(self, attr), subattr, value) def fdel(self): delattr(getattr(self, attr), subattr)
- return property(**filter_dict(locals().copy(), ['attr', 'subattr']))
- return property(fget, fset if not ro else None, fdel if not ro else None, doc)
+def ro_subprop(attr, subattr, doc = None):
- return prop(attr, subattr, doc, True)
diff --git a/imagefactory/qmfagent/ImageFactoryAgent.py b/imagefactory/qmfagent/ImageFactoryAgent.py index 73cd1db..0017cec 100644 --- a/imagefactory/qmfagent/ImageFactoryAgent.py +++ b/imagefactory/qmfagent/ImageFactoryAgent.py @@ -26,13 +26,7 @@ from imagefactory import props class ImageFactoryAgent(AgentHandler):
qmf_object = props.prop("_qmf_object", "The qmf_object property.")
- def managedObjects():
doc = "The managedObjects property."
def fget(self):
return self._managedObjects
return locals()
- managedObjects = property(**managedObjects())
managedObjects = props.ro_prop("_managedObjects", "The managedObjects property.")
def __init__(self, url): self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
-- 1.7.4.4
--- imagefactory/ApplicationConfiguration.py | 23 ++++++------------- imagefactory/Singleton.py | 35 ++++++++++++++++++++++++++++++ imagefactory/qmfagent/ImageFactory.py | 21 ++++++------------ imgfac.py | 34 +++++++++++------------------ 4 files changed, 62 insertions(+), 51 deletions(-) create mode 100644 imagefactory/Singleton.py
diff --git a/imagefactory/ApplicationConfiguration.py b/imagefactory/ApplicationConfiguration.py index 9de2b78..3b121b8 100644 --- a/imagefactory/ApplicationConfiguration.py +++ b/imagefactory/ApplicationConfiguration.py @@ -24,27 +24,18 @@ import argparse import json import logging import props +from Singleton import Singleton
-class ApplicationConfiguration(object): - instance = None - +class ApplicationConfiguration(Singleton): configuration = props.prop("_configuration", "The configuration property.")
- def __new__(cls, *p, **k): - if cls.instance is None: - i = super(ApplicationConfiguration, cls).__new__(cls, *p, **k) - #initialize here, not in __init__() - i.log = logging.getLogger('%s.%s' % (__name__, i.__class__.__name__)) - - i.configuration = i.__parse_arguments() - - cls.instance = i - elif(len(p) | len(k) > 0): - cls.instance.log.warn('Attempted re-initialize of singleton: %s' % (cls.instance, )) - return cls.instance + def _singleton_init(self): + super(ApplicationConfiguration, self)._singleton_init() + self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) + self.configuration = self.__parse_arguments()
def __init__(self): - pass + pass
def __new_argument_parser(self): main_description = """Image Factory is an application for creating system images to run virtual machines in various public and private \ diff --git a/imagefactory/Singleton.py b/imagefactory/Singleton.py new file mode 100644 index 0000000..161da6f --- /dev/null +++ b/imagefactory/Singleton.py @@ -0,0 +1,35 @@ +# 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. + +class Singleton(object): + _instance = None + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + instance = super(Singleton, cls).__new__(cls, *args, **kwargs) + instance._singleton_init() + cls._instance = instance + elif args or kwargs: + cls._instance.log.warn('Attempted re-initialize of singleton: %s' % (cls._instance, )) + return cls._instance + + def __init__(self): + pass + + def _singleton_init(self): + """Initialize a singleton instance before it is registered.""" + pass diff --git a/imagefactory/qmfagent/ImageFactory.py b/imagefactory/qmfagent/ImageFactory.py index a0c2bcc..c5e64fa 100644 --- a/imagefactory/qmfagent/ImageFactory.py +++ b/imagefactory/qmfagent/ImageFactory.py @@ -24,13 +24,13 @@ from BuildAdaptor import BuildAdaptor from imagefactory import props from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.Singleton import Singleton from imagefactory.Template import Template import logging
# Singleton representing the Factory itself
-class ImageFactory(object): - instance = None +class ImageFactory(Singleton):
# QMF schema for ImageFactory qmf_schema = Schema(SCHEMA_TYPE_DATA, "com.redhat.imagefactory", "ImageFactory") @@ -77,18 +77,11 @@ class ImageFactory(object): qmf_object = props.prop("_qmf_object", "The qmf_object property.") agent = props.prop("_agent", "The property agent")
- def __new__(cls, *p, **k): - if cls.instance is None: - i = super(ImageFactory, cls).__new__(cls, *p, **k) - # initialize here, not in __init__() - i.log = logging.getLogger('%s.%s' % (__name__, i.__class__.__name__)) - i.qmf_object = Data(ImageFactory.qmf_schema) - #i.agent = k.get('agent', p[0] if (len(p) > 0) else None) - i.warehouse = ImageWarehouse(ApplicationConfiguration().configuration["warehouse"]) - cls.instance = i - elif(len(p) | len(k) > 0): - cls.instance.log.warn('Attempted re-initialize of singleton: %s' % (cls.instance, )) - return cls.instance + def _singleton_init(self): + super(ImageFactory, self)._singleton_init() + self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) + self.qmf_object = Data(ImageFactory.qmf_schema) + self.warehouse = ImageWarehouse(ApplicationConfiguration().configuration["warehouse"])
def __init__(self): pass diff --git a/imgfac.py b/imgfac.py index 30e1fea..8b1ba11 100755 --- a/imgfac.py +++ b/imgfac.py @@ -27,9 +27,9 @@ from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.qmfagent.ImageFactoryAgent import * from imagefactory.BuildDispatcher import BuildDispatcher from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.Singleton import Singleton
-class Application(object): - instance = None +class Application(Singleton):
def qmf_agent(): doc = "The qmf_agent property." @@ -42,28 +42,20 @@ class Application(object): return locals() qmf_agent = property(**qmf_agent())
- def __new__(cls, *p, **k): - if cls.instance is None: - i = super(Application, cls).__new__(cls, *p, **k) - # initialize here, not in __init__() - i.log = logging.getLogger('%s.%s' % (__name__, i.__class__.__name__)) - i.daemon = False - i.pid_file_path = "/var/run/imagefactory.pid" - signal.signal(signal.SIGTERM, i.signal_handler) - i.app_config = ApplicationConfiguration().configuration - - # by setting TMPDIR here we make sure that libguestfs - # (imagefactory -> oz -> libguestfs) uses the temporary directory of - # the user's choosing - os.putenv('TMPDIR', i.app_config['tmpdir']) - - cls.instance = i - elif(len(p) | len(k) > 0): - cls.instance.log.warn('Attempted re-initialize of singleton: %s' % (cls.instance, )) - return cls.instance + def _singleton_init(self): + super(Application, self)._singleton_init() + self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) + self.daemon = False + self.pid_file_path = "/var/run/imagefactory.pid" + signal.signal(signal.SIGTERM, self.signal_handler) + self.app_config = ApplicationConfiguration().configuration + + # by setting TMPDIR here we make sure that libguestfs + # (imagefactory -> oz -> libguestfs) uses the temporary directory of + # the user's choosing + os.putenv('TMPDIR', self.app_config['tmpdir'])
def __init__(self): - # logging.basicConfig(level=logging.NOTSET, format='%(asctime)s %(levelname)s %(name)s pid(%(process)d) Message: %(message)s') pass
def setup_logging(self):
ACK
You probably guessed I don't think in terms of multiple inheritance. -steve
On Jun 15, 2011, at 6:45 AM, Mark McLoughlin wrote:
imagefactory/ApplicationConfiguration.py | 23 ++++++------------- imagefactory/Singleton.py | 35 ++++++++++++++++++++++++++++++ imagefactory/qmfagent/ImageFactory.py | 21 ++++++------------ imgfac.py | 34 +++++++++++------------------ 4 files changed, 62 insertions(+), 51 deletions(-) create mode 100644 imagefactory/Singleton.py
diff --git a/imagefactory/ApplicationConfiguration.py b/imagefactory/ApplicationConfiguration.py index 9de2b78..3b121b8 100644 --- a/imagefactory/ApplicationConfiguration.py +++ b/imagefactory/ApplicationConfiguration.py @@ -24,27 +24,18 @@ import argparse import json import logging import props +from Singleton import Singleton
-class ApplicationConfiguration(object):
- instance = None
+class ApplicationConfiguration(Singleton): configuration = props.prop("_configuration", "The configuration property.")
- def __new__(cls, *p, **k):
if cls.instance is None:
i = super(ApplicationConfiguration, cls).__new__(cls, *p, **k)
#initialize here, not in __init__()
i.log = logging.getLogger('%s.%s' % (__name__, i.__class__.__name__))
i.configuration = i.__parse_arguments()
cls.instance = i
elif(len(p) | len(k) > 0):
cls.instance.log.warn('Attempted re-initialize of singleton: %s' % (cls.instance, ))
return cls.instance
def _singleton_init(self):
super(ApplicationConfiguration, self)._singleton_init()
self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
self.configuration = self.__parse_arguments()
def __init__(self):
pass
pass
def __new_argument_parser(self): main_description = """Image Factory is an application for creating system images to run virtual machines in various public and private \
diff --git a/imagefactory/Singleton.py b/imagefactory/Singleton.py new file mode 100644 index 0000000..161da6f --- /dev/null +++ b/imagefactory/Singleton.py @@ -0,0 +1,35 @@ +# 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.
+class Singleton(object):
- _instance = None
- def __new__(cls, *args, **kwargs):
if cls._instance is None:
instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
instance._singleton_init()
cls._instance = instance
elif args or kwargs:
cls._instance.log.warn('Attempted re-initialize of singleton: %s' % (cls._instance, ))
return cls._instance
- def __init__(self):
pass
- def _singleton_init(self):
"""Initialize a singleton instance before it is registered."""
pass
diff --git a/imagefactory/qmfagent/ImageFactory.py b/imagefactory/qmfagent/ImageFactory.py index a0c2bcc..c5e64fa 100644 --- a/imagefactory/qmfagent/ImageFactory.py +++ b/imagefactory/qmfagent/ImageFactory.py @@ -24,13 +24,13 @@ from BuildAdaptor import BuildAdaptor from imagefactory import props from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.Singleton import Singleton from imagefactory.Template import Template import logging
# Singleton representing the Factory itself
-class ImageFactory(object):
- instance = None
+class ImageFactory(Singleton):
# QMF schema for ImageFactory qmf_schema = Schema(SCHEMA_TYPE_DATA, "com.redhat.imagefactory", "ImageFactory")
@@ -77,18 +77,11 @@ class ImageFactory(object): qmf_object = props.prop("_qmf_object", "The qmf_object property.") agent = props.prop("_agent", "The property agent")
- def __new__(cls, *p, **k):
if cls.instance is None:
i = super(ImageFactory, cls).__new__(cls, *p, **k)
# initialize here, not in __init__()
i.log = logging.getLogger('%s.%s' % (__name__, i.__class__.__name__))
i.qmf_object = Data(ImageFactory.qmf_schema)
#i.agent = k.get('agent', p[0] if (len(p) > 0) else None)
i.warehouse = ImageWarehouse(ApplicationConfiguration().configuration["warehouse"])
cls.instance = i
elif(len(p) | len(k) > 0):
cls.instance.log.warn('Attempted re-initialize of singleton: %s' % (cls.instance, ))
return cls.instance
def _singleton_init(self):
super(ImageFactory, self)._singleton_init()
self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
self.qmf_object = Data(ImageFactory.qmf_schema)
self.warehouse = ImageWarehouse(ApplicationConfiguration().configuration["warehouse"])
def __init__(self): pass
diff --git a/imgfac.py b/imgfac.py index 30e1fea..8b1ba11 100755 --- a/imgfac.py +++ b/imgfac.py @@ -27,9 +27,9 @@ from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.qmfagent.ImageFactoryAgent import * from imagefactory.BuildDispatcher import BuildDispatcher from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.Singleton import Singleton
-class Application(object):
- instance = None
+class Application(Singleton):
def qmf_agent(): doc = "The qmf_agent property."
@@ -42,28 +42,20 @@ class Application(object): return locals() qmf_agent = property(**qmf_agent())
- def __new__(cls, *p, **k):
if cls.instance is None:
i = super(Application, cls).__new__(cls, *p, **k)
# initialize here, not in __init__()
i.log = logging.getLogger('%s.%s' % (__name__, i.__class__.__name__))
i.daemon = False
i.pid_file_path = "/var/run/imagefactory.pid"
signal.signal(signal.SIGTERM, i.signal_handler)
i.app_config = ApplicationConfiguration().configuration
# by setting TMPDIR here we make sure that libguestfs
# (imagefactory -> oz -> libguestfs) uses the temporary directory of
# the user's choosing
os.putenv('TMPDIR', i.app_config['tmpdir'])
cls.instance = i
elif(len(p) | len(k) > 0):
cls.instance.log.warn('Attempted re-initialize of singleton: %s' % (cls.instance, ))
return cls.instance
def _singleton_init(self):
super(Application, self)._singleton_init()
self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
self.daemon = False
self.pid_file_path = "/var/run/imagefactory.pid"
signal.signal(signal.SIGTERM, self.signal_handler)
self.app_config = ApplicationConfiguration().configuration
# by setting TMPDIR here we make sure that libguestfs
# (imagefactory -> oz -> libguestfs) uses the temporary directory of
# the user's choosing
os.putenv('TMPDIR', self.app_config['tmpdir'])
def __init__(self):
# logging.basicConfig(level=logging.NOTSET, format='%(asctime)s %(levelname)s %(name)s pid(%(process)d) Message: %(message)s') pass
def setup_logging(self):
-- 1.7.4.4
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()
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
--- imagefactory/BuildDispatcher.py | 73 +++++++++++++-------------------- imagefactory/qmfagent/ImageFactory.py | 4 +- imgfac.py | 4 +- 3 files changed, 33 insertions(+), 48 deletions(-)
diff --git a/imagefactory/BuildDispatcher.py b/imagefactory/BuildDispatcher.py index a1cfc9d..4b0bd00 100644 --- a/imagefactory/BuildDispatcher.py +++ b/imagefactory/BuildDispatcher.py @@ -20,26 +20,26 @@ import os.path from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.BuildJob import BuildJob from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.Singleton import Singleton from imagefactory.Template import Template
# # TODO: # - Split the watcher code out into separate modules -# - Make use of a singleton instead of class methods #
-class BuildDispatcher(object): +class BuildDispatcher(Singleton):
- @classmethod - def build_image_for_targets(cls, image_id, build_id, template, targets, job_cls = BuildJob, *args, **kwargs): - warehouse = cls._get_warehouse() + def _singleton_init(self): + self.warehouse = ImageWarehouse(ApplicationConfiguration().configuration['warehouse'])
+ def build_image_for_targets(self, image_id, build_id, template, targets, job_cls = BuildJob, *args, **kwargs): template = Template(template)
- image_id = cls._ensure_image(warehouse, image_id, template) - build_id = cls._ensure_build(warehouse, image_id, build_id) + image_id = self._ensure_image(image_id, template) + build_id = self._ensure_build(image_id, build_id)
- watcher = BuildWatcher(image_id, build_id, len(targets), warehouse) + watcher = BuildWatcher(image_id, build_id, len(targets), self.warehouse)
jobs = [] for target in targets: @@ -49,22 +49,19 @@ class BuildDispatcher(object):
return jobs
- @classmethod - def push_image_to_providers(cls, image_id, build_id, providers, credentials, job_cls = BuildJob, *args, **kwargs): - warehouse = cls._get_warehouse() - + def push_image_to_providers(self, image_id, build_id, providers, credentials, job_cls = BuildJob, *args, **kwargs): if not build_id: - build_id = cls._latest_unpushed(warehouse, image_id) + build_id = self._latest_unpushed(image_id)
- watcher = PushWatcher(image_id, build_id, len(providers), warehouse) + watcher = PushWatcher(image_id, build_id, len(providers), self.warehouse)
jobs = [] for provider in providers: - target = cls._map_provider_to_target(provider) + target = self._map_provider_to_target(provider)
- target_image_id = cls._target_image_for_build_and_target(warehouse, build_id, target) + target_image_id = self._target_image_for_build_and_target(build_id, target)
- template = cls._template_for_target_image_id(warehouse, target_image_id) + template = self._template_for_target_image_id(target_image_id)
job = job_cls(template, target, image_id, build_id, *args, **kwargs) job.push_image(target_image_id, provider, credentials, watcher) @@ -72,50 +69,39 @@ class BuildDispatcher(object):
return jobs
- @classmethod - def _get_warehouse(cls): - return ImageWarehouse(ApplicationConfiguration().configuration['warehouse']) - - @classmethod - def _xml_node(cls, xml, xpath): + def _xml_node(self, xml, xpath): nodes = libxml2.parseDoc(xml).xpathEval(xpath) if not nodes: return None return nodes[0].content
- @classmethod - def _ensure_image(cls, warehouse, image_id, template): + def _ensure_image(self, image_id, template): if image_id: return image_id
- name = cls._xml_node(template.xml, '/template/name') + name = self._xml_node(template.xml, '/template/name') if name: image_xml = '<image><name>%s</name></image>' % name else: image_xml = '</image>'
- return warehouse.store_image(None, image_xml) + return self.warehouse.store_image(None, image_xml)
- @classmethod - def _ensure_build(cls, warehouse, image_id, build_id): + def _ensure_build(self, image_id, build_id): if build_id: return build_id - return warehouse.store_build(None, dict(image = image_id)) + return self.warehouse.store_build(None, dict(image = image_id))
- @classmethod - def _latest_unpushed(cls, warehouse, image_id): - return warehouse.metadata_for_id_of_type(['latest_unpushed'], image_id, 'image')['latest_unpushed'] + def _latest_unpushed(self, image_id): + return self.warehouse.metadata_for_id_of_type(['latest_unpushed'], image_id, 'image')['latest_unpushed']
- @classmethod - def _target_image_for_build_and_target(cls, warehouse, build_id, target): - return warehouse.query("target_image", "$build == "%s" && $target == "%s"" % (build_id, target))[0] + def _target_image_for_build_and_target(self, build_id, target): + return self.warehouse.query("target_image", "$build == "%s" && $target == "%s"" % (build_id, target))[0]
- @classmethod - def _template_for_target_image_id(cls, warehouse, target_image_id): - return warehouse.metadata_for_id_of_type(['template'], target_image_id, 'target_image')['template'] + def _template_for_target_image_id(self, target_image_id): + return self.warehouse.metadata_for_id_of_type(['template'], target_image_id, 'target_image')['template']
- @classmethod - def _is_rhevm_provider(cls, provider): + def _is_rhevm_provider(self, provider): rhevm_json = '/etc/rhevm.json' if not os.path.exists(rhevm_json): return False @@ -142,13 +128,12 @@ class BuildDispatcher(object): # - mock: any provider with 'mock' prefix # - rackspace: provider is rackspace # - @classmethod - def _map_provider_to_target(cls, provider): + def _map_provider_to_target(self, provider): if provider.startswith('ec2-'): return 'ec2' elif provider == 'rackspace': return 'rackspace' - elif cls._is_rhevm_provider(provider): + elif self._is_rhevm_provider(provider): return 'rhev-m' elif provider.startswith('mock'): return 'mock' diff --git a/imagefactory/qmfagent/ImageFactory.py b/imagefactory/qmfagent/ImageFactory.py index 460883c..e167775 100644 --- a/imagefactory/qmfagent/ImageFactory.py +++ b/imagefactory/qmfagent/ImageFactory.py @@ -108,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 BuildDispatcher.build_image_for_targets(image, build, template, targets, BuildAdaptor, self.agent) + return BuildDispatcher().build_image_for_targets(image, build, template, targets, BuildAdaptor, self.agent)
def push_image(self, image, build, providers, credentials): - return BuildDispatcher.push_image_to_providers(image, build, providers, credentials, BuildAdaptor, 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/imgfac.py b/imgfac.py index 8b1ba11..0a7562a 100755 --- a/imgfac.py +++ b/imgfac.py @@ -148,7 +148,7 @@ class Application(Singleton): self.setup_logging()
if (self.app_config['template'] and self.app_config['target']): - dispatchers = BuildDispatcher.build_image_for_targets(self.app_config['image'], None, self.app_config['template'], self.app_config['target']) + dispatchers = BuildDispatcher().build_image_for_targets(self.app_config['image'], None, self.app_config['template'], self.app_config['target']) for dispatcher in dispatchers: print("Building build %s of image %s to target %s" % (dispatcher.build_id, dispatcher.image_id, dispatcher.target))
@@ -164,7 +164,7 @@ class Application(Singleton): print("Unexpected content or formatting of credentials...") sys.exit(1)
- dispatchers = BuildDispatcher.push_image_to_providers(self.app_config['image'], None, self.app_config['provider'], credentials) + dispatchers = BuildDispatcher().push_image_to_providers(self.app_config['image'], None, self.app_config['provider'], credentials) for dispatcher in dispatchers: print("Pushing build %s of image %s to provider %s" % (dispatcher.build_id, dispatcher.image_id, dispatcher.provider))
ACK
On Jun 15, 2011, at 6:45 AM, Mark McLoughlin wrote:
imagefactory/BuildDispatcher.py | 73 +++++++++++++-------------------- imagefactory/qmfagent/ImageFactory.py | 4 +- imgfac.py | 4 +- 3 files changed, 33 insertions(+), 48 deletions(-)
diff --git a/imagefactory/BuildDispatcher.py b/imagefactory/BuildDispatcher.py index a1cfc9d..4b0bd00 100644 --- a/imagefactory/BuildDispatcher.py +++ b/imagefactory/BuildDispatcher.py @@ -20,26 +20,26 @@ import os.path from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.BuildJob import BuildJob from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.Singleton import Singleton from imagefactory.Template import Template
# # TODO: # - Split the watcher code out into separate modules -# - Make use of a singleton instead of class methods #
-class BuildDispatcher(object): +class BuildDispatcher(Singleton):
- @classmethod
- def build_image_for_targets(cls, image_id, build_id, template, targets, job_cls = BuildJob, *args, **kwargs):
warehouse = cls._get_warehouse()
def _singleton_init(self):
self.warehouse = ImageWarehouse(ApplicationConfiguration().configuration['warehouse'])
def build_image_for_targets(self, image_id, build_id, template, targets, job_cls = BuildJob, *args, **kwargs): template = Template(template)
image_id = cls._ensure_image(warehouse, image_id, template)
build_id = cls._ensure_build(warehouse, image_id, build_id)
image_id = self._ensure_image(image_id, template)
build_id = self._ensure_build(image_id, build_id)
watcher = BuildWatcher(image_id, build_id, len(targets), warehouse)
watcher = BuildWatcher(image_id, build_id, len(targets), self.warehouse) jobs = [] for target in targets:
@@ -49,22 +49,19 @@ class BuildDispatcher(object):
return jobs
- @classmethod
- def push_image_to_providers(cls, image_id, build_id, providers, credentials, job_cls = BuildJob, *args, **kwargs):
warehouse = cls._get_warehouse()
- def push_image_to_providers(self, image_id, build_id, providers, credentials, job_cls = BuildJob, *args, **kwargs): if not build_id:
build_id = cls._latest_unpushed(warehouse, image_id)
build_id = self._latest_unpushed(image_id)
watcher = PushWatcher(image_id, build_id, len(providers), warehouse)
watcher = PushWatcher(image_id, build_id, len(providers), self.warehouse) jobs = [] for provider in providers:
target = cls._map_provider_to_target(provider)
target = self._map_provider_to_target(provider)
target_image_id = cls._target_image_for_build_and_target(warehouse, build_id, target)
target_image_id = self._target_image_for_build_and_target(build_id, target)
template = cls._template_for_target_image_id(warehouse, target_image_id)
template = self._template_for_target_image_id(target_image_id) job = job_cls(template, target, image_id, build_id, *args, **kwargs) job.push_image(target_image_id, provider, credentials, watcher)
@@ -72,50 +69,39 @@ class BuildDispatcher(object):
return jobs
- @classmethod
- def _get_warehouse(cls):
return ImageWarehouse(ApplicationConfiguration().configuration['warehouse'])
- @classmethod
- def _xml_node(cls, xml, xpath):
- def _xml_node(self, xml, xpath): nodes = libxml2.parseDoc(xml).xpathEval(xpath) if not nodes: return None return nodes[0].content
- @classmethod
- def _ensure_image(cls, warehouse, image_id, template):
- def _ensure_image(self, image_id, template): if image_id: return image_id
name = cls._xml_node(template.xml, '/template/name')
name = self._xml_node(template.xml, '/template/name') if name: image_xml = '<image><name>%s</name></image>' % name else: image_xml = '</image>'
return warehouse.store_image(None, image_xml)
return self.warehouse.store_image(None, image_xml)
- @classmethod
- def _ensure_build(cls, warehouse, image_id, build_id):
- def _ensure_build(self, image_id, build_id): if build_id: return build_id
return warehouse.store_build(None, dict(image = image_id))
return self.warehouse.store_build(None, dict(image = image_id))
- @classmethod
- def _latest_unpushed(cls, warehouse, image_id):
return warehouse.metadata_for_id_of_type(['latest_unpushed'], image_id, 'image')['latest_unpushed']
- def _latest_unpushed(self, image_id):
return self.warehouse.metadata_for_id_of_type(['latest_unpushed'], image_id, 'image')['latest_unpushed']
- @classmethod
- def _target_image_for_build_and_target(cls, warehouse, build_id, target):
return warehouse.query("target_image", "$build == \"%s\" && $target == \"%s\"" % (build_id, target))[0]
- def _target_image_for_build_and_target(self, build_id, target):
return self.warehouse.query("target_image", "$build == \"%s\" && $target == \"%s\"" % (build_id, target))[0]
- @classmethod
- def _template_for_target_image_id(cls, warehouse, target_image_id):
return warehouse.metadata_for_id_of_type(['template'], target_image_id, 'target_image')['template']
- def _template_for_target_image_id(self, target_image_id):
return self.warehouse.metadata_for_id_of_type(['template'], target_image_id, 'target_image')['template']
- @classmethod
- def _is_rhevm_provider(cls, provider):
- def _is_rhevm_provider(self, provider): rhevm_json = '/etc/rhevm.json' if not os.path.exists(rhevm_json): return False
@@ -142,13 +128,12 @@ class BuildDispatcher(object): # - mock: any provider with 'mock' prefix # - rackspace: provider is rackspace #
- @classmethod
- def _map_provider_to_target(cls, provider):
- def _map_provider_to_target(self, provider): if provider.startswith('ec2-'): return 'ec2' elif provider == 'rackspace': return 'rackspace'
elif cls._is_rhevm_provider(provider):
elif self._is_rhevm_provider(provider): return 'rhev-m' elif provider.startswith('mock'): return 'mock'
diff --git a/imagefactory/qmfagent/ImageFactory.py b/imagefactory/qmfagent/ImageFactory.py index 460883c..e167775 100644 --- a/imagefactory/qmfagent/ImageFactory.py +++ b/imagefactory/qmfagent/ImageFactory.py @@ -108,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 BuildDispatcher.build_image_for_targets(image, build, template, targets, BuildAdaptor, self.agent)
return BuildDispatcher().build_image_for_targets(image, build, template, targets, BuildAdaptor, self.agent)
def push_image(self, image, build, providers, credentials):
return BuildDispatcher.push_image_to_providers(image, build, providers, credentials, BuildAdaptor, 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/imgfac.py b/imgfac.py index 8b1ba11..0a7562a 100755 --- a/imgfac.py +++ b/imgfac.py @@ -148,7 +148,7 @@ class Application(Singleton): self.setup_logging()
if (self.app_config['template'] and self.app_config['target']):
dispatchers = BuildDispatcher.build_image_for_targets(self.app_config['image'], None, self.app_config['template'], self.app_config['target'])
dispatchers = BuildDispatcher().build_image_for_targets(self.app_config['image'], None, self.app_config['template'], self.app_config['target']) for dispatcher in dispatchers: print("Building build %s of image %s to target %s" % (dispatcher.build_id, dispatcher.image_id, dispatcher.target))
@@ -164,7 +164,7 @@ class Application(Singleton): print("Unexpected content or formatting of credentials...") sys.exit(1)
dispatchers = BuildDispatcher.push_image_to_providers(self.app_config['image'], None, self.app_config['provider'], credentials)
dispatchers = BuildDispatcher().push_image_to_providers(self.app_config['image'], None, self.app_config['provider'], credentials) for dispatcher in dispatchers: print("Pushing build %s of image %s to provider %s" % (dispatcher.build_id, dispatcher.image_id, dispatcher.provider))
-- 1.7.4.4
--- imagefactory/BaseWatcher.py | 52 +++++++++++++++++++++++++++++++++++ imagefactory/BuildDispatcher.py | 57 +------------------------------------- imagefactory/BuildWatcher.py | 27 ++++++++++++++++++ imagefactory/PushWatcher.py | 23 +++++++++++++++ 4 files changed, 104 insertions(+), 55 deletions(-) create mode 100644 imagefactory/BaseWatcher.py create mode 100644 imagefactory/BuildWatcher.py create mode 100644 imagefactory/PushWatcher.py
diff --git a/imagefactory/BaseWatcher.py b/imagefactory/BaseWatcher.py new file mode 100644 index 0000000..b2f45b6 --- /dev/null +++ b/imagefactory/BaseWatcher.py @@ -0,0 +1,52 @@ +# 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. + +class BaseWatcher(object): + def __init__(self, image_id, build_id, remaining, warehouse): + self.remaining = remaining + self.warehouse = warehouse + self.image_id = image_id + self.build_id = build_id + + def completed(self): + self.remaining -= 1 + if self.remaining == 0: + self.all_completed() + + def all_completed(self): + pass + + def _image_attr(self, attr): + return self.warehouse.metadata_for_id_of_type([attr], self.image_id, 'image')[attr] + + def _set_image_attr(self, attr, value): + self.warehouse.set_metadata_for_id_of_type({attr : value}, self.image_id, 'image') + + def _latest_build(self): + return self._image_attr('latest_build') + + def _set_latest_build(self, build_id): + self._set_image_attr('latest_build', build_id) + + def _latest_unpushed(self): + return self._image_attr('latest_unpushed') + + def _set_latest_unpushed(self, build_id): + self._set_image_attr('latest_unpushed', build_id) + + def _set_build_parent(self, parent_id): + self.warehouse.set_metadata_for_id_of_type({'parent' : parent_id}, self.build_id, 'build') diff --git a/imagefactory/BuildDispatcher.py b/imagefactory/BuildDispatcher.py index 4b0bd00..f2317d1 100644 --- a/imagefactory/BuildDispatcher.py +++ b/imagefactory/BuildDispatcher.py @@ -19,15 +19,12 @@ import libxml2 import os.path from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.BuildJob import BuildJob +from imagefactory.BuildWatcher import BuildWatcher from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.PushWatcher import PushWatcher from imagefactory.Singleton import Singleton from imagefactory.Template import Template
-# -# TODO: -# - Split the watcher code out into separate modules -# - class BuildDispatcher(Singleton):
def _singleton_init(self): @@ -139,53 +136,3 @@ class BuildDispatcher(Singleton): return 'mock' else: return 'condorcloud' # condorcloud ignores provider - -class Watcher(object): - def __init__(self, image_id, build_id, remaining, warehouse): - self.remaining = remaining - self.warehouse = warehouse - self.image_id = image_id - self.build_id = build_id - - def completed(self): - self.remaining -= 1 - if self.remaining == 0: - self.all_completed() - - def all_completed(self): - pass - - def _image_attr(self, attr): - return self.warehouse.metadata_for_id_of_type([attr], self.image_id, 'image')[attr] - - def _set_image_attr(self, attr, value): - self.warehouse.set_metadata_for_id_of_type({attr : value}, self.image_id, 'image') - - def _latest_build(self): - return self._image_attr('latest_build') - - def _set_latest_build(self, build_id): - self._set_image_attr('latest_build', build_id) - - def _latest_unpushed(self): - return self._image_attr('latest_unpushed') - - def _set_latest_unpushed(self, build_id): - self._set_image_attr('latest_unpushed', build_id) - - def _set_build_parent(self, parent_id): - self.warehouse.set_metadata_for_id_of_type({'parent' : parent_id}, self.build_id, 'build') - -class BuildWatcher(Watcher): - def all_completed(self): - parent_id = self._latest_unpushed() - if not parent_id: - parent_id = self._latest_build() - self._set_latest_unpushed(self.build_id) - if parent_id: - self._set_build_parent(parent_id) - -class PushWatcher(Watcher): - def all_completed(self): - self._set_latest_build(self.build_id) - self._set_latest_unpushed(None) diff --git a/imagefactory/BuildWatcher.py b/imagefactory/BuildWatcher.py new file mode 100644 index 0000000..6d17132 --- /dev/null +++ b/imagefactory/BuildWatcher.py @@ -0,0 +1,27 @@ +# 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 BaseWatcher + +class BuildWatcher(BaseWatcher.BaseWatcher): + def all_completed(self): + parent_id = self._latest_unpushed() + if not parent_id: + parent_id = self._latest_build() + self._set_latest_unpushed(self.build_id) + if parent_id: + self._set_build_parent(parent_id) diff --git a/imagefactory/PushWatcher.py b/imagefactory/PushWatcher.py new file mode 100644 index 0000000..cdf0034 --- /dev/null +++ b/imagefactory/PushWatcher.py @@ -0,0 +1,23 @@ +# 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 BaseWatcher + +class PushWatcher(BaseWatcher.BaseWatcher): + def all_completed(self): + self._set_latest_build(self.build_id) + self._set_latest_unpushed(None)
Sorry, you only said 5 patches... this is 6.
ACK
On Jun 15, 2011, at 6:50 AM, Mark McLoughlin wrote:
imagefactory/BaseWatcher.py | 52 +++++++++++++++++++++++++++++++++++ imagefactory/BuildDispatcher.py | 57 +------------------------------------- imagefactory/BuildWatcher.py | 27 ++++++++++++++++++ imagefactory/PushWatcher.py | 23 +++++++++++++++ 4 files changed, 104 insertions(+), 55 deletions(-) create mode 100644 imagefactory/BaseWatcher.py create mode 100644 imagefactory/BuildWatcher.py create mode 100644 imagefactory/PushWatcher.py
diff --git a/imagefactory/BaseWatcher.py b/imagefactory/BaseWatcher.py new file mode 100644 index 0000000..b2f45b6 --- /dev/null +++ b/imagefactory/BaseWatcher.py @@ -0,0 +1,52 @@ +# 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.
+class BaseWatcher(object):
- def __init__(self, image_id, build_id, remaining, warehouse):
self.remaining = remaining
self.warehouse = warehouse
self.image_id = image_id
self.build_id = build_id
- def completed(self):
self.remaining -= 1
if self.remaining == 0:
self.all_completed()
- def all_completed(self):
pass
- def _image_attr(self, attr):
return self.warehouse.metadata_for_id_of_type([attr], self.image_id, 'image')[attr]
- def _set_image_attr(self, attr, value):
self.warehouse.set_metadata_for_id_of_type({attr : value}, self.image_id, 'image')
- def _latest_build(self):
return self._image_attr('latest_build')
- def _set_latest_build(self, build_id):
self._set_image_attr('latest_build', build_id)
- def _latest_unpushed(self):
return self._image_attr('latest_unpushed')
- def _set_latest_unpushed(self, build_id):
self._set_image_attr('latest_unpushed', build_id)
- def _set_build_parent(self, parent_id):
self.warehouse.set_metadata_for_id_of_type({'parent' : parent_id}, self.build_id, 'build')
diff --git a/imagefactory/BuildDispatcher.py b/imagefactory/BuildDispatcher.py index 4b0bd00..f2317d1 100644 --- a/imagefactory/BuildDispatcher.py +++ b/imagefactory/BuildDispatcher.py @@ -19,15 +19,12 @@ import libxml2 import os.path from imagefactory.ApplicationConfiguration import ApplicationConfiguration from imagefactory.BuildJob import BuildJob +from imagefactory.BuildWatcher import BuildWatcher from imagefactory.ImageWarehouse import ImageWarehouse +from imagefactory.PushWatcher import PushWatcher from imagefactory.Singleton import Singleton from imagefactory.Template import Template
-# -# TODO: -# - Split the watcher code out into separate modules -#
class BuildDispatcher(Singleton):
def _singleton_init(self):
@@ -139,53 +136,3 @@ class BuildDispatcher(Singleton): return 'mock' else: return 'condorcloud' # condorcloud ignores provider
-class Watcher(object):
- def __init__(self, image_id, build_id, remaining, warehouse):
self.remaining = remaining
self.warehouse = warehouse
self.image_id = image_id
self.build_id = build_id
- def completed(self):
self.remaining -= 1
if self.remaining == 0:
self.all_completed()
- def all_completed(self):
pass
- def _image_attr(self, attr):
return self.warehouse.metadata_for_id_of_type([attr], self.image_id, 'image')[attr]
- def _set_image_attr(self, attr, value):
self.warehouse.set_metadata_for_id_of_type({attr : value}, self.image_id, 'image')
- def _latest_build(self):
return self._image_attr('latest_build')
- def _set_latest_build(self, build_id):
self._set_image_attr('latest_build', build_id)
- def _latest_unpushed(self):
return self._image_attr('latest_unpushed')
- def _set_latest_unpushed(self, build_id):
self._set_image_attr('latest_unpushed', build_id)
- def _set_build_parent(self, parent_id):
self.warehouse.set_metadata_for_id_of_type({'parent' : parent_id}, self.build_id, 'build')
-class BuildWatcher(Watcher):
- def all_completed(self):
parent_id = self._latest_unpushed()
if not parent_id:
parent_id = self._latest_build()
self._set_latest_unpushed(self.build_id)
if parent_id:
self._set_build_parent(parent_id)
-class PushWatcher(Watcher):
- def all_completed(self):
self._set_latest_build(self.build_id)
self._set_latest_unpushed(None)
diff --git a/imagefactory/BuildWatcher.py b/imagefactory/BuildWatcher.py new file mode 100644 index 0000000..6d17132 --- /dev/null +++ b/imagefactory/BuildWatcher.py @@ -0,0 +1,27 @@ +# 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 BaseWatcher
+class BuildWatcher(BaseWatcher.BaseWatcher):
- def all_completed(self):
parent_id = self._latest_unpushed()
if not parent_id:
parent_id = self._latest_build()
self._set_latest_unpushed(self.build_id)
if parent_id:
self._set_build_parent(parent_id)
diff --git a/imagefactory/PushWatcher.py b/imagefactory/PushWatcher.py new file mode 100644 index 0000000..cdf0034 --- /dev/null +++ b/imagefactory/PushWatcher.py @@ -0,0 +1,23 @@ +# 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 BaseWatcher
+class PushWatcher(BaseWatcher.BaseWatcher):
- def all_completed(self):
self._set_latest_build(self.build_id)
self._set_latest_unpushed(None)
-- 1.7.4.4
On Wed, 2011-06-15 at 13:58 -0500, Steve Loranz wrote:
Sorry, you only said 5 patches... this is 6.
Yeah, sorry about that. I sent the 5 before realizing this last patch belonged in this series too
Thanks for all the acks, I've pushed now
Cheers, Mark.
aeolus-devel@lists.fedorahosted.org