summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBill Peck <bpeck@redhat.com>2011-03-12 02:00:32 +1000
committerGerrit Code Review <gerrit@maeby.djc.id.au>2011-03-12 02:00:32 +1000
commit1b36e7fd9b5d67829980ec6f282a89a334c79717 (patch)
tree5775dd68f959ccb8449af2c9337fcbb1e2642f01
parent321fdee24465cbc46d4239207630c8889b3dc9b7 (diff)
parentfb1850981b65a33f42301102c25d41b786ab6cf9 (diff)
Merge "introduce a new reservation table" into develop
-rw-r--r--Server/bkr/server/controllers.py2
-rw-r--r--Server/bkr/server/model.py94
-rw-r--r--Server/bkr/server/systems.py2
-rw-r--r--Server/bkr/server/test/data_setup.py5
-rw-r--r--Server/bkr/server/test/selenium/test_systems_xmlrpc.py19
-rw-r--r--Server/bkr/server/test/test_beakerd.py58
-rwxr-xr-xServer/bkr/server/tools/beakerd.py26
-rw-r--r--Server/bkr/server/user.py2
8 files changed, 137 insertions, 71 deletions
diff --git a/Server/bkr/server/controllers.py b/Server/bkr/server/controllers.py
index 36a4826..b03518a 100644
--- a/Server/bkr/server/controllers.py
+++ b/Server/bkr/server/controllers.py
@@ -1065,7 +1065,7 @@ class Root(RPCRoot):
flash(_(u'Failed to return %s: %s') % (system.fqdn, e))
else:
try:
- system.reserve(service=u'WEBUI')
+ system.reserve(service=u'WEBUI', reservation_type=u'manual')
flash(_(u'Reserved %s') % system.fqdn)
except BeakerException, e:
log.exception('Failed to reserve')
diff --git a/Server/bkr/server/model.py b/Server/bkr/server/model.py
index b7e2f24..58676be 100644
--- a/Server/bkr/server/model.py
+++ b/Server/bkr/server/model.py
@@ -750,6 +750,19 @@ log_recipe_task_result_table = Table('log_recipe_task_result', metadata,
mysql_engine='InnoDB',
)
+reservation_table = Table('reservation', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('system_id', Integer, ForeignKey('system.id'), nullable=False),
+ Column('user_id', Integer, ForeignKey('tg_user.user_id'),
+ nullable=False),
+ Column('start_time', DateTime, index=True, nullable=False,
+ default=datetime.utcnow),
+ Column('finish_time', DateTime, index=True),
+ # type = 'manual' or 'recipe'
+ Column('type', Unicode(30), index=True, nullable=False),
+ mysql_engine='InnoDB',
+)
+
recipe_table = Table('recipe',metadata,
Column('id', Integer, primary_key=True),
Column('recipe_set_id', Integer,
@@ -1970,7 +1983,7 @@ url --url=$tree
This represents the basic system perms,loanee, owner, shared and in group or shared and no group
"""
try:
- if identity.in_group('admin'):
+ if u'admin' in user.groups:
return True
except AttributeError, e: #not logged in ?
return False
@@ -2264,7 +2277,6 @@ url --url=$tree
self.remote.release()
except:
pass
- self.user = None
def action_provision(self,
distro=None,
@@ -2435,36 +2447,61 @@ $SNIPPET("rhts_post")
old_value=old_status,
new_value=self.status))
- def reserve(self, service):
- if self.user is not None and self.user == identity.current.user:
+ def reserve(self, service, user=None, reservation_type=u'manual'):
+ if user is None:
+ user = identity.current.user
+ if self.user is not None and self.user == user:
raise BX(_(u'User %s has already reserved system %s')
- % (identity.current.user, self))
- if not self.can_share(identity.current.user):
+ % (user, self))
+ if not self.can_share(user):
raise BX(_(u'User %s cannot reserve system %s')
- % (identity.current.user, self))
+ % (user, self))
# Atomic operation to reserve the system
+ session.flush()
if session.connection(System).execute(system_table.update(
and_(system_table.c.id == self.id,
system_table.c.user_id == None)),
- user_id=identity.current.user.user_id).rowcount == 1:
- self.activity.append(SystemActivity(user=identity.current.user,
- service=service, action=u'Reserved', field_name=u'User',
- old_value=u'', new_value=identity.current.user))
- else:
- raise BX(_(u'System is already reserved'))
+ user_id=user.user_id).rowcount != 1:
+ raise BX(_(u'System %r is already reserved') % self)
+ self.user = user # do it here too, so that the ORM is aware
+ self.reservations.append(Reservation(user=user, type=reservation_type))
+ self.activity.append(SystemActivity(user=user,
+ service=service, action=u'Reserved', field_name=u'User',
+ old_value=u'', new_value=user.user_name))
+ log.debug('Created reservation for system %r with type %r, service %r, user %r',
+ self, reservation_type, service, user)
+
+ def unreserve(self, service, user=None, watchdog=None):
+ if user is None:
+ user = identity.current.user
- def unreserve(self, service):
+ # Some sanity checks
if self.user is None:
raise BX(_(u'System is not reserved'))
- if not self.current_user(identity.current.user):
+ if not self.current_user(user):
raise BX(_(u'System is reserved by a different user'))
- # Don't return a system with an active watchdog
- if self.watchdog:
- # This won't really happen anymore since the Manual/Automated split
+
+ # Watchdog checking -- don't return a system with an active watchdog,
+ # unless the correct watchdog has been passed in to be cleared
+ if self.watchdog and self.watchdog == watchdog:
+ session.delete(self.watchdog)
+ elif self.watchdog and watchdog is None:
raise BX(_(u'System has active recipe %s') % self.watchdog.recipe_id)
- activity = SystemActivity(user=identity.current.user,
+ elif self.watchdog or watchdog:
+ raise BX(_(u'Mismatched watchdogs in unreserve: %r, %r')
+ % (self.watchdog, watchdog)) # should never happen
+
+ # Update reservation atomically first, to avoid races
+ session.flush()
+ if session.connection(System).execute(reservation_table.update(
+ and_(reservation_table.c.system_id == self.id,
+ reservation_table.c.finish_time == None)),
+ finish_time=datetime.utcnow()).rowcount != 1:
+ raise BX(_(u'System does not have an open reservation'))
+ activity = SystemActivity(user=user,
service=service, action=u'Returned', field_name=u'User',
old_value=self.user.user_name, new_value=u'')
+ self.user = None
try:
self.action_release()
except BX, e:
@@ -4937,15 +4974,9 @@ class Recipe(TaskBase):
log.debug("Remove watchdog for recipe %s" % self.id)
if self.watchdog.system == self.system:
try:
- self.system.action_release()
log.debug("Return system %s for recipe %s" % (self.system, self.id))
- self.system.activity.append(
- SystemActivity(self.recipeset.job.owner,
- 'Scheduler',
- 'Returned',
- 'User',
- '%s' % self.recipeset.job.owner,
- ''))
+ self.system.unreserve(service=u'Scheduler',
+ user=self.recipeset.job.owner, watchdog=self.watchdog)
except socket.gaierror, error:
#FIXME
pass
@@ -4954,7 +4985,6 @@ class Recipe(TaskBase):
pass
except AttributeError, error:
pass
- del(self.watchdog)
def task_info(self):
"""
@@ -5895,6 +5925,8 @@ class TaskBugzilla(MappedObject):
"""
pass
+class Reservation(MappedObject): pass
+
# set up mappers between identity tables and classes
SystemType.mapper = mapper(SystemType, system_type_table)
SystemStatus.mapper = mapper(SystemStatus, system_status_table)
@@ -6247,6 +6279,12 @@ mapper(RecipeTaskResult, recipe_task_result_table,
mapper(TaskPriority, task_priority_table)
mapper(TaskStatus, task_status_table)
mapper(TaskResult, task_result_table)
+mapper(Reservation, reservation_table, properties={
+ 'system': relation(System, backref=backref('reservations',
+ order_by=[reservation_table.c.start_time.desc()])),
+ 'user': relation(User, backref=backref('reservations',
+ order_by=[reservation_table.c.start_time.desc()])),
+})
## Static list of device_classes -- used by master.kid
global _device_classes
diff --git a/Server/bkr/server/systems.py b/Server/bkr/server/systems.py
index e93e566..65ef395 100644
--- a/Server/bkr/server/systems.py
+++ b/Server/bkr/server/systems.py
@@ -33,7 +33,7 @@ class SystemsController(controllers.Controller):
system = System.by_fqdn(fqdn, identity.current.user)
if system.status != SystemStatus.by_name(u'Manual'):
raise BX(_(u'Cannot reserve system with status %s') % system.status)
- system.reserve(service=u'XMLRPC')
+ system.reserve(service=u'XMLRPC', reservation_type=u'manual')
return system.fqdn # because turbogears makes us return something
@expose()
diff --git a/Server/bkr/server/test/data_setup.py b/Server/bkr/server/test/data_setup.py
index cc6c871..9960fc2 100644
--- a/Server/bkr/server/test/data_setup.py
+++ b/Server/bkr/server/test/data_setup.py
@@ -263,8 +263,9 @@ def mark_job_waiting(job, user=None):
recipe.process()
recipe.queue()
recipe.schedule()
- recipe.system = create_system()
- recipe.system.user = user
+ recipe.system = create_system(owner=job.owner)
+ recipe.system.reserve(service=u'testdata', user=job.owner,
+ reservation_type=u'recipe')
recipe.watchdog = Watchdog(system=recipe.system)
recipe.waiting()
diff --git a/Server/bkr/server/test/selenium/test_systems_xmlrpc.py b/Server/bkr/server/test/selenium/test_systems_xmlrpc.py
index e07e598..09e8fae 100644
--- a/Server/bkr/server/test/selenium/test_systems_xmlrpc.py
+++ b/Server/bkr/server/test/selenium/test_systems_xmlrpc.py
@@ -84,6 +84,9 @@ class ReserveSystemXmlRpcTest(XmlRpcTestCase):
server.systems.reserve(system.fqdn)
session.refresh(system)
self.assertEqual(system.user, user)
+ self.assertEqual(system.reservations[0].type, u'manual')
+ self.assertEqual(system.reservations[0].user, user)
+ self.assert_(system.reservations[0].finish_time is None)
reserved_activity = system.activity[-1]
self.assertEqual(reserved_activity.action, 'Reserved')
self.assertEqual(reserved_activity.field_name, 'User')
@@ -123,6 +126,9 @@ class ReserveSystemXmlRpcTest(XmlRpcTestCase):
server.systems.reserve(system.fqdn)
session.refresh(system)
self.assertEqual(system.user, user)
+ self.assertEqual(system.reservations[0].type, u'manual')
+ self.assertEqual(system.reservations[0].user, user)
+ self.assert_(system.reservations[0].finish_time is None)
reserved_activity = system.activity[0]
self.assertEqual(reserved_activity.action, 'Reserved')
self.assertEqual(reserved_activity.service, service_user.user_name)
@@ -146,7 +152,7 @@ class ReleaseSystemXmlRpcTest(XmlRpcTestCase):
status=u'Manual', shared=True)
user = data_setup.create_user(password=u'password')
other_user = data_setup.create_user()
- system.user = other_user
+ system.reserve(service=u'testdata', user=other_user)
session.flush()
server = self.get_server()
server.auth.login_password(user.user_name, 'password')
@@ -162,14 +168,19 @@ class ReleaseSystemXmlRpcTest(XmlRpcTestCase):
owner=User.by_user_name(data_setup.ADMIN_USER),
status=u'Manual', shared=True)
user = data_setup.create_user(password=u'password')
- system.user = user
+ system.reserve(service=u'testdata', user=user)
session.flush()
server = self.get_server()
server.auth.login_password(user.user_name, 'password')
server.systems.release(system.fqdn)
session.refresh(system)
+ session.refresh(system.reservations[0])
self.assert_(system.user is None)
- released_activity = system.activity[-1]
+ self.assertEquals(system.reservations[0].user, user)
+ assert_datetime_within(system.reservations[0].finish_time,
+ tolerance=datetime.timedelta(seconds=10),
+ reference=datetime.datetime.utcnow())
+ released_activity = system.activity[0]
self.assertEqual(released_activity.action, 'Returned')
self.assertEqual(released_activity.field_name, 'User')
self.assertEqual(released_activity.user, user)
@@ -182,7 +193,7 @@ class ReleaseSystemXmlRpcTest(XmlRpcTestCase):
owner=User.by_user_name(data_setup.ADMIN_USER),
status=u'Manual', shared=True)
user = data_setup.create_user(password=u'password')
- system.user = user
+ system.reserve(service=u'testdata', user=user)
session.flush()
server = self.get_server()
server.auth.login_password(user.user_name, 'password')
diff --git a/Server/bkr/server/test/test_beakerd.py b/Server/bkr/server/test/test_beakerd.py
index 9cd0e4b..259e698 100644
--- a/Server/bkr/server/test/test_beakerd.py
+++ b/Server/bkr/server/test/test_beakerd.py
@@ -1,10 +1,11 @@
import unittest
-
+import datetime
from time import sleep
-from bkr.server.model import TaskStatus, Job
+from bkr.server.model import TaskStatus, Job, System, User
import sqlalchemy.orm
from turbogears.database import session
from bkr.server.test import data_setup
+from bkr.server.test.assertions import assert_datetime_within
from bkr.server.tools import beakerd
import threading
@@ -15,16 +16,8 @@ class TestBeakerd(unittest.TestCase):
data_setup.create_test_env('min')
session.flush()
- def setUp(self):
- self.jobs = list()
- distro = data_setup.create_distro()
- for i in range(30):
- job = data_setup.create_job(whiteboard=u'job_%s' % i, distro=distro)
- self.jobs.append(job)
- session.flush()
-
- def _check_job_status(self,status):
- for j in self.jobs:
+ def _check_job_status(self, jobs, status):
+ for j in jobs:
job = Job.by_id(j.id)
self.assertEqual(job.status,TaskStatus.by_name(status))
@@ -33,6 +26,12 @@ class TestBeakerd(unittest.TestCase):
TaskStatus.by_name(u'Queued')
def test_cache_new_to_queued(self):
+ jobs = list()
+ distro = data_setup.create_distro()
+ for i in range(30):
+ job = data_setup.create_job(whiteboard=u'job_%s' % i, distro=distro)
+ jobs.append(job)
+ session.flush()
self._create_cached_status()
#We need to run our beakerd methods as threads to ensure
@@ -50,15 +49,40 @@ class TestBeakerd(unittest.TestCase):
thread_new_recipe.join()
self.assertTrue(thread_new_recipe.success)
session.clear()
- self._check_job_status(u'Processed')
+ self._check_job_status(jobs, u'Processed')
thread_processed_recipe = Do(target=beakerd.processed_recipesets)
thread_processed_recipe.start()
thread_processed_recipe.join()
self.assertTrue(thread_processed_recipe.success)
session.clear()
- self._check_job_status(u'Queued')
+ self._check_job_status(jobs, u'Queued')
- @classmethod
- def teardownClass(cls):
- pass
+ def test_reservations_are_created(self):
+ data_setup.create_task(name=u'/distribution/install')
+ user = data_setup.create_user()
+ lc = data_setup.create_labcontroller()
+ distro = data_setup.create_distro()
+ system = data_setup.create_system(owner=user, status=u'Automated', shared=True)
+ system.lab_controller = lc
+ job = data_setup.create_job(owner=user, distro=distro)
+ job.recipesets[0].recipes[0]._host_requires = (
+ '<hostRequires><hostname op="=" value="%s"/></hostRequires>'
+ % system.fqdn)
+ session.flush()
+ session.clear()
+
+ beakerd.new_recipes()
+ beakerd.processed_recipesets()
+ beakerd.queued_recipes()
+
+ job = Job.query().get(job.id)
+ system = System.query().get(system.id)
+ user = User.query().get(user.user_id)
+ self.assertEqual(job.status, TaskStatus.by_name(u'Scheduled'))
+ self.assertEqual(system.reservations[0].type, u'recipe')
+ self.assertEqual(system.reservations[0].user, user)
+ assert_datetime_within(system.reservations[0].start_time,
+ tolerance=datetime.timedelta(seconds=60),
+ reference=datetime.datetime.utcnow())
+ self.assert_(system.reservations[0].finish_time is None)
diff --git a/Server/bkr/server/tools/beakerd.py b/Server/bkr/server/tools/beakerd.py
index 489a370..dc37f2e 100755
--- a/Server/bkr/server/tools/beakerd.py
+++ b/Server/bkr/server/tools/beakerd.py
@@ -374,23 +374,15 @@ def queued_recipes(*args):
else:
recipe.schedule()
recipe.createRepo()
- if session.connection(Recipe).execute(system_table.update(
- and_(system_table.c.id==system.id,
- system_table.c.user_id==None)),
- user_id=recipe.recipeset.job.owner.user_id).rowcount == 1:
- recipe.system = system
- recipe.recipeset.lab_controller = system.lab_controller
- recipe.systems = []
- # Create the watchdog without an Expire time.
- log.debug("Created watchdog for recipe id: %s and system: %s" % (recipe.id, system))
- recipe.watchdog = Watchdog(system=recipe.system)
- activity = SystemActivity(recipe.recipeset.job.owner, "Scheduler", "Reserved", "User", "", "%s" % recipe.recipeset.job.owner )
- system.activity.append(activity)
- log.info("recipe ID %s moved from Queued to Scheduled" % recipe.id)
- else:
- # The system was taken from underneath us. Put recipe
- # back into queued state and try again.
- raise BX(_('System %s was stolen from underneath us. will try again.' % system))
+ system.reserve(service=u'Scheduler', user=recipe.recipeset.job.owner,
+ reservation_type=u'recipe')
+ recipe.system = system
+ recipe.recipeset.lab_controller = system.lab_controller
+ recipe.systems = []
+ # Create the watchdog without an Expire time.
+ log.debug("Created watchdog for recipe id: %s and system: %s" % (recipe.id, system))
+ recipe.watchdog = Watchdog(system=recipe.system)
+ log.info("recipe ID %s moved from Queued to Scheduled" % recipe.id)
session.commit()
except exceptions.Exception:
session.rollback()
diff --git a/Server/bkr/server/user.py b/Server/bkr/server/user.py
index 1cee76e..bd6c41a 100644
--- a/Server/bkr/server/user.py
+++ b/Server/bkr/server/user.py
@@ -130,7 +130,7 @@ class Users(AdminPage):
for system in System.query().filter(System.user==user):
msg = ''
try:
- system.action_release()
+ system.unreserve(service=method, user=user)
except BX, error_msg:
msg = 'Error: %s Action: %s' % (error_msg,system.release_action)
system.activity.append(SystemActivity(identity.current.user, method, '%s' % system.release_action, 'Return', '', msg))