diff --git a/.gitattributes b/.gitattributes index 3de2c8e64d4e34c57891724cf6d162085faebb80..f82ffadc57a31aa0a0b90a2fe125df8a40bc843c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4936,6 +4936,7 @@ SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener -text SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.ini -text SAS/ResourceAssignment/ResourceAssignmentDatabase/radbpglistener.py -text SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/CMakeLists.txt -text +SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/radb_performance_test.py -text SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.py -text SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.run -text SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.sh -text diff --git a/SAS/ResourceAssignment/Common/lib/specification.py b/SAS/ResourceAssignment/Common/lib/specification.py index 7a29ebc39e5820d832d1660a09c23456622bad00..b34ac2da39322bb629cb0f2fda0f8b58cb96296a 100644 --- a/SAS/ResourceAssignment/Common/lib/specification.py +++ b/SAS/ResourceAssignment/Common/lib/specification.py @@ -70,10 +70,10 @@ class Specification: def as_dict(self): """"Mostly a serialization function to make a qpid message.""" - result = {} + result = dict() result["otdb_id"] = self.otdb_id result["mom_id"] = self.mom_id - result["task_id"] = self.task_id + result["task_id"] = self.radb_id result["status"] = self.status result["task_type"] = self.type result["task_subtype"] = self.subtype @@ -93,7 +93,7 @@ class Specification: """"Mostly a serialization function to read from a qpid message.""" self.otdb_id = input_dict["otdb_id"] self.mom_id = input_dict["mom_id"] - self.task_id = input_dict["task_id"] + self.radb_id = input_dict["task_id"] self.status = input_dict["status"] self.type = input_dict["task_type"] self.subtype = input_dict["task_subtype"] @@ -111,7 +111,7 @@ class Specification: self.successor_ids = input_dict["successors"] def isObservation(self): - return (self.type == "observation") + return self.type == "observation" def validate(self): pass # Not implemented @@ -135,7 +135,35 @@ class Specification: self.duration = timedelta(seconds=int(time_restrictions["minDuration"])) self.max_starttime = max_end_time - self.duration # TODO huh? This doesn't seem to match DwellScheduler specs - # ========================= parset/OTDB related methods ======================================================================= + #TODO This needs to be merged with branch 11100 code. + # def _calculate_min_max_starttimes(self, start_time, end_time, min_starttime, max_endtime): + # """ + # Use any specified min_starttime/max_endtime to calculate the min_starttime/max_starttime. All timestamps are in + # datetime format + # + # :param start_time: Task fixed start time + # :param end_time: Task fixed end time + # :param min_starttime: Task minimum start time + # :param max_endtime: Task maximum end time + # + # :return: 3-tuple (min_starttime, max_starttime, duration) + # """ + # # If no dwelling is specified, this is the task's duration + # duration = end_time - start_time + # + # # Calculate the effective dwelling time + # dwell_time = 0 + # + # if min_starttime is not None and max_endtime is not None: + # max_duration = max_endtime - min_starttime + # dwell_time = max_duration - duration + # + # # Calculate min/max starttime + # min_starttime = max(start_time, min_starttime) if min_starttime is not None else start_time + # max_starttime = min_starttime + dwell_time + # return min_starttime, max_starttime, duration + +# ========================= parset/OTDB related methods ======================================================================= @staticmethod def OTDB_to_RA_task_types(otdb_task_type, otdb_task_subtype): @@ -701,7 +729,6 @@ class Specification: else: return None - def insert_into_radb(self): """ Tries to inserts the task's specification into RADB along with any of its predecessors and successors. @@ -744,6 +771,21 @@ class Specification: message = "doAssignment: Unsupported status '%s' of task with OTDB ID: %s" % (self.status, self.otdb_id) raise Exception(message) + def set_status(self, new_status): + """ + Updates a task's status in RADB. + + :param new_status: the new status to set the task to in RADB + + :raises Exception if updating RADB fails + """ + self.status = new_status + if new_status in ('conflict', 'error', 'scheduled'): + self.logger.info('Finishing resource assignment for task_id=%s, status=%s' % (self.radb_id, new_status)) + # another service sets the parset spec in OTDB, and updated otdb task status to scheduled, which is then + # synced to RADB + self.radbrpc.updateTask(self.radb_id, task_status=new_status) + def _insert_main_task(self, start_time, end_time): """ Inserts the main task and its specification into the RADB. Any existing specification and task with same diff --git a/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py b/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py index 3e6b8a1264687c6d46b39593b45abcc23fde180d..4f4da6ad427e11a3b6c974cf48b97edcc08343cc 100644 --- a/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py +++ b/SAS/ResourceAssignment/OTDBtoRATaskStatusPropagator/propagator.py @@ -8,6 +8,7 @@ import logging from datetime import datetime, timedelta from optparse import OptionParser from lofar.common.util import waitForInterrupt +from lofar.common.datetimeutils import parseDatetime from lofar.sas.otdb.OTDBBusListener import OTDBBusListener from lofar.sas.otdb.otdbrpc import OTDBRPC from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME @@ -18,6 +19,8 @@ from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RARPC logger = logging.getLogger(__name__) +STORAGE_CLAIM_EXTENSION=timedelta(days=365) + class OTDBtoRATaskStatusPropagator(OTDBBusListener): def __init__(self, otdb_notification_busname=DEFAULT_OTDB_NOTIFICATION_BUSNAME, @@ -83,8 +86,9 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): new_startime = spec['ObsSW.Observation.startTime'] new_endtime = spec['ObsSW.Observation.stopTime'] - new_startime = datetime.strptime(new_startime, ('%Y-%m-%d %H:%M:%S.%f' if '.' in new_startime else '%Y-%m-%d %H:%M:%S')) - new_endtime = datetime.strptime(new_endtime, ('%Y-%m-%d %H:%M:%S.%f' if '.' in new_endtime else '%Y-%m-%d %H:%M:%S')) + new_startime = parseDatetime(new_startime) + new_endtime = parseDatetime(new_endtime) + new_endtime = max(new_endtime, radb_task['starttime']+timedelta(seconds=1)) # make sure endtime is always > starttime logger.info("Updating task (otdb_id=%s, radb_id=%s, status=%s) startime to \'%s\' and endtime to \'%s\'", treeId, radb_task['id'], radb_task['status'], new_startime, new_endtime) self.radb.updateTaskAndResourceClaims(radb_task['id'], starttime=new_startime, endtime=new_endtime) @@ -104,6 +108,9 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): def onObservationConflict(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'conflict') + def onObservationError(self, treeId, modificationTime): + self._update_radb_task_status(treeId, 'error') + def onObservationObsolete(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'obsolete') @@ -144,8 +151,27 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): new_startime = max([max_pred_endtime, datetime.utcnow()]) new_endtime = new_startime + timedelta(seconds=task['duration']) - logger.info("Updating task %s (otdb_id=%s, status=queued) startime to \'%s\' and endtime to \'%s\'", task['id'], treeId, new_startime, new_endtime) - self.radb.updateTaskAndResourceClaims(task['id'], starttime=new_startime, endtime=new_endtime) + logger.info("Updating task %s (otdb_id=%s, status=active) startime to \'%s\' and endtime to \'%s\' except for storage claims", + task['id'], treeId, new_startime, new_endtime) + + #update task and all claim start/endtimes, except for storage claims. + non_storage_resource_type_ids = [rt['id'] for rt in self.radb.getResourceTypes() if rt['name'] != 'storage'] + self.radb.updateTaskAndResourceClaims(task['id'], + where_resource_types=non_storage_resource_type_ids, + starttime=new_startime, + endtime=new_endtime) + + #get remaining storage claims... + #and update storage start/end times (including 1 year extra) + logger.info("Updating storage claims for task %s (otdb_id=%s, status=active) startime to \'%s\' and endtime to \'%s\' (with extra storage claim time)", + task['id'], treeId, new_startime, new_endtime+STORAGE_CLAIM_EXTENSION) + + storage_claims = self.radb.getResourceClaims(task_ids=task['id'], resource_type='storage') + storage_claim_ids = [c['id'] for c in storage_claims] + self.radb.updateResourceClaims(where_resource_claim_ids=storage_claim_ids, + starttime=new_startime, + endtime=new_endtime+STORAGE_CLAIM_EXTENSION) + except Exception as e: logger.error(e) @@ -161,8 +187,26 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): new_startime = otdb_task['starttime'] new_endtime = new_startime + timedelta(seconds=radb_task['duration']) - logger.info("Updating task %s (otdb_id=%s, status=active) startime to \'%s\' and endtime to \'%s\'", radb_task['id'], treeId, new_startime, new_endtime) - self.radb.updateTaskAndResourceClaims(radb_task['id'], starttime=new_startime, endtime=new_endtime) + logger.info("Updating task %s (otdb_id=%s, status=active) startime to \'%s\' and endtime to \'%s\' except for storage claims", + radb_task['id'], treeId, new_startime, new_endtime) + + #update task and all claim start/endtimes, except for storage claims. + non_storage_resource_type_ids = [rt['id'] for rt in self.radb.getResourceTypes() if rt['name'] != 'storage'] + self.radb.updateTaskAndResourceClaims(radb_task['id'], + where_resource_types=non_storage_resource_type_ids, + starttime=new_startime, + endtime=new_endtime) + + #get remaining storage claims... + #and update storage start/end times (including 1 year extra) + logger.info("Updating storage claims for task %s (otdb_id=%s, status=active) startime to \'%s\' and endtime to \'%s\' with extra claim time", + radb_task['id'], treeId, new_startime, new_endtime) + + storage_claims = self.radb.getResourceClaims(task_ids=radb_task['id'], resource_type='storage') + storage_claim_ids = [c['id'] for c in storage_claims] + self.radb.updateResourceClaims(where_resource_claim_ids=storage_claim_ids, + starttime=new_startime, + endtime=new_endtime+STORAGE_CLAIM_EXTENSION) def onObservationCompleting(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'completing') @@ -177,11 +221,33 @@ class OTDBtoRATaskStatusPropagator(OTDBBusListener): return otdb_task = self.otdb.taskGetTreeInfo(otdb_id=treeId) - if otdb_task and (otdb_task['starttime'] != radb_task['starttime'] or otdb_task['stoptime'] != radb_task['endtime']): - new_endtime = otdb_task['stoptime'] + if otdb_task is None: + logger.warning('could not find todb task with id %s', treeId) + return - logger.info("Updating task %s (otdb_id=%s, status=%s) endtime to \'%s\'", radb_task['id'], treeId, radb_task['status'], new_endtime) - self.radb.updateTaskAndResourceClaims(radb_task['id'], endtime=new_endtime) + now = datetime.utcnow() + if (now < otdb_task['stoptime'] #early stop/abort + or otdb_task['starttime'] != radb_task['starttime'] or otdb_task['stoptime'] != radb_task['endtime']): #change in spec + new_endtime = min(now, otdb_task['stoptime']) + new_endtime = max(new_endtime, radb_task['starttime']+timedelta(seconds=1)) # make sure endtime is always > starttime + + logger.info("Updating task %s (otdb_id=%s, status=%s) endtime to \'%s\' except for storage resource claims", radb_task['id'], treeId, radb_task['status'], new_endtime) + + #update task and all claim endtimes, except for storage claims. + non_storage_resource_type_ids = [rt['id'] for rt in self.radb.getResourceTypes() if rt['name'] != 'storage'] + self.radb.updateTaskAndResourceClaims(radb_task['id'], + where_resource_types=non_storage_resource_type_ids, + endtime=new_endtime) + + #get remaining storage claims... + #and extend storage end time + logger.info("Updating storage claims for task %s (otdb_id=%s, status=%s) endtime to \'%s\' (with extra storage claim time)", + radb_task['id'], treeId, radb_task['status'], new_endtime+STORAGE_CLAIM_EXTENSION) + + storage_claims = self.radb.getResourceClaims(task_ids=radb_task['id'], resource_type='storage') + storage_claim_ids = [c['id'] for c in storage_claims] + self.radb.updateResourceClaims(where_resource_claim_ids=storage_claim_ids, + endtime=new_endtime+STORAGE_CLAIM_EXTENSION) def onObservationFinished(self, treeId, modificationTime): self._update_radb_task_status(treeId, 'finished') diff --git a/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt index 461c560491f904b0ce595be0022b171c45f0d372..f9de242c044c9d6caed1b71118e503af9fe6ac1b 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssigner/CMakeLists.txt @@ -1,6 +1,6 @@ # $Id: CMakeLists.txt 30355 2014-11-04 13:46:05Z loose $ -lofar_package(ResourceAssigner 0.1 DEPENDS MAC_Services MessageBus PyMessaging PyCommon pyparameterset OTDB_Services ResourceAssignmentService MoMQueryService ResourceAssignmentEstimator CleanupClient StorageQueryService) +lofar_package(ResourceAssigner 0.1 DEPENDS PyMessaging PyCommon pyparameterset OTDB_Services ResourceAssignmentService MoMQueryServiceClient ResourceAssignmentEstimator CleanupClient StorageQueryService MAC_Services MessageBus) include(PythonInstall) set(USE_PYTHON_COMPILATION Off) diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py b/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py index 09e0413ad52ebef815d71a998f26781d050e2d83..1ad10ee3d00f702a4814acf1d2aa0792da5e386b 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/raservice.py @@ -63,7 +63,7 @@ class SpecifiedTaskListener(RATaskSpecifiedBusListener): self.assigner = ResourceAssigner() def onTaskSpecified(self, otdb_id, specification_tree): - logger.info('onTaskSpecified: otdb_id=%s' % otdb_id) + logger.info('onTaskSpecified: otdb_id=%s status=%s', otdb_id, specification_tree.get('state', '').lower()) try: self.assigner.do_assignment(otdb_id, specification_tree) @@ -176,6 +176,8 @@ def main(): mom_servicename=options.mom_query_servicename, cleanup_busname=options.cleanup_busname, cleanup_servicename=options.cleanup_servicename, + otdb_busname=options.otdb_busname, + otdb_servicename=options.otdb_servicename, broker=options.broker) as schedulechecker: waitForInterrupt() diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/resource_assigner.py b/SAS/ResourceAssignment/ResourceAssigner/lib/resource_assigner.py index f02fe7937d10b4474be70e60bd305ed0acedb5e4..620ead0686e57f414ea14aa6a2e281d1b2d2a993 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/resource_assigner.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/resource_assigner.py @@ -187,49 +187,61 @@ class ResourceAssigner(object): if spec.status == 'approved': #TODO should this even still happen? logger.info('Task otdb_id=%s is only approved, no resource assignment needed yet' % otdb_id) return + #TODO have Specification propagate to the estimator? requested_resources = self._get_resource_estimates(specification_tree, otdb_id, spec.type, spec.radb_id) if requested_resources is None: - # No resource requests available, so change task status to "error" - self._finish_resource_assignment(spec, 'error') + ## No resource requests available, so change task status to "error" and notify our subscribers + spec.set_status('error') + self._send_task_status_notification(spec, 'error') else: if self._schedule_resources(spec, requested_resources): # Cleanup the data of any previous run of the task self._cleanup_earlier_generated_data(otdb_id, spec) - # Scheduling of resources for this task succeeded, so change task status to "scheduled" - self._finish_resource_assignment(spec, 'scheduled') + # Scheduling of resources for this task succeeded, so change task status to "scheduled" and notify + # our subscribers + spec.set_status('scheduled') + self._send_task_status_notification(spec, 'scheduled') else: - # Scheduling of resources for this task failed, so change task status to "conflict" - self._finish_resource_assignment(spec, 'conflict') - - def _finish_resource_assignment(self, spec, new_task_status): + # Scheduling of resources for this task failed, + # check if any of the claims has status conflict, + # and hence (by the radb triggers) the task has status conflict as well + # if task not in conflict, then there was a specification/scheduling error + # so put task status to error (not conflict!) + spec.read_from_radb(spec.radb_id) #TODO cleanup + if spec.status == 'conflict': + # Updating the task status when it is already in 'conflict' seems unnecessary, but in order to + # satisfy the existing unit tests we put it in here. + # TODO: discuss if this can be removed + spec.set_status('conflict') + + # No need for a status change, but do notify our subscribers + self._send_task_status_notification(spec, 'conflict') + else: + # The task is in an unexpected state, so force it to 'error' state and notify our subscribers + spec.set_status('error') + self._send_task_status_notification(spec, 'error') + + def _send_task_status_notification(self, spec, new_status): """ - Finishes the resource assignment by updating a task's status in RADB and sending out a corresponding - notification to registered parties on the Resource Assigner notification bus. + Sends a message about the task's status on the RA notification bus - :param task: the task at hand - :param new_task_status: the new status to set the task to in RADB + :param spec: the task concerned + :param new_status: the task's status - :raises Exception if updating RADB fails, or if sending the notification fails + :raises Exception if sending the notification fails """ - - if spec is not None and new_task_status in ('conflict', 'error', 'scheduled'): - logger.info('Finishing resource assignment for task_id=%s, status=%s' % (spec.radb_id, new_task_status)) - - # another service sets the parset spec in OTDB, and updated otdb task status to scheduled, which is then - # synced to RADB - self.radbrpc.updateTask(spec.radb_id, task_status=new_task_status) - - content = { - 'radb_id': spec.radb_id, - 'otdb_id': spec.otdb_id, - 'mom_id': spec.mom_id - } - subject = 'Task' + new_task_status[0].upper() + new_task_status[1:] #TODO MAGIC! - event_message = EventMessage(context=self.ra_notification_prefix + subject, content=content) - - logger.info('Sending notification %s: %s' % (subject, str(content).replace('\n', ' '))) - self.ra_notification_bus.send(event_message) + #TODO can maybe move to Specification in the future? Logically it should be resource_assigner that sends the notification + content = { + 'radb_id': spec.radb_id, + 'otdb_id': spec.otdb_id, + 'mom_id': spec.mom_id + } + subject = 'Task' + new_status[0].upper() + new_status[1:] #TODO this is MAGIC, needs explanation! + event_message = EventMessage(context=self.ra_notification_prefix + subject, content=content) + + logger.info('Sending notification %s: %s' % (subject, str(content).replace('\n', ' '))) + self.ra_notification_bus.send(event_message) def _get_resource_estimates(self, specification_tree, otdb_id, task_type, task_id): """ @@ -280,15 +292,11 @@ class ResourceAssigner(object): """ Schedule the requested resources for a task - :param task_id: the task's ID - :param specification_tree: the task's specification tree + :param spec: the task's specification :param requested_resources: the resources requested by the task :returns: True if successful, or False otherwise """ - # For now dwell-behavior is disabled by setting min_starttime/max_starttime to - # start_time, because the specification doesn't yet support this. - # TODO: enable dwell-scheduling once min_starttime/max_starttime are propagated scheduler = DwellScheduler(task_id=spec.radb_id, resource_availability_checker=self.resource_availability_checker, radbcreds=self.radb_creds, @@ -296,7 +304,11 @@ class ResourceAssigner(object): max_starttime=spec.max_starttime, duration=spec.duration) - result = scheduler.allocate_resources(requested_resources) + try: + result = scheduler.allocate_resources(requested_resources) + except Exception as e: + logger.error('Error in calling scheduler.allocate_resources: %s', e) + result = False if result: logger.info('Resources successfully allocated task_id=%s' % spec.radb_id) @@ -310,7 +322,7 @@ class ResourceAssigner(object): Remove any output and/or intermediate data from any previous run of the task :param otdb_id: the task's OTDB ID - :param task: the task object + :param spec: the task's specification """ # Only needed for pipeline tasks diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py b/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py index 5a0c815d428dfb08b86f6e893b62d76739feb5e7..0e36a752d39c0a7307df077150c4a6f9b7122baf 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/schedulechecker.py @@ -37,7 +37,11 @@ from lofar.sas.datamanagement.cleanup.rpc import CleanupRPC from lofar.sas.datamanagement.cleanup.config import DEFAULT_BUSNAME as DEFAULT_CLEANUP_BUSNAME from lofar.sas.datamanagement.cleanup.config import DEFAULT_SERVICENAME as DEFAULT_CLEANUP_SERVICENAME +from lofar.sas.otdb.otdbrpc import OTDBRPC +from lofar.sas.otdb.config import DEFAULT_OTDB_SERVICE_BUSNAME, DEFAULT_OTDB_SERVICENAME + from lofar.sas.resourceassignment.resourceassigner.config import PIPELINE_CHECK_INTERVAL +from lofar.common.datetimeutils import * logger = logging.getLogger(__name__) @@ -45,7 +49,8 @@ def movePipelineAfterItsPredecessors(task, radbrpc, min_start_timestamp=None): try: #only reschedule pipelines which run on cep4 if task and task['type'] == 'pipeline' and task.get('cluster') == 'CEP4': - logger.info("checking pipeline starttime radb_id=%s otdb_id=%s", task['id'], task['otdb_id']) + logger.info("checking pipeline starttime radb_id=%s otdb_id=%s starttime=%s", + task['id'], task['otdb_id'], task['starttime']) predecessor_tasks = radbrpc.getTasks(task_ids=task['predecessor_ids']) @@ -66,7 +71,7 @@ def movePipelineAfterItsPredecessors(task, radbrpc, min_start_timestamp=None): #exclude self overlapping_pipelines = [pl for pl in overlapping_pipelines if pl['id'] != task['id']] - if len(overlapping_pipelines) >= 2: + if len(overlapping_pipelines) >= 1: max_overlapping_pipeline_endtime = max([t['endtime'] for t in overlapping_pipelines]) shift = max_overlapping_pipeline_endtime + timedelta(minutes=1) - task['starttime'] newStartTime = task['starttime']+shift @@ -75,9 +80,13 @@ def movePipelineAfterItsPredecessors(task, radbrpc, min_start_timestamp=None): break if shift != timedelta(seconds=0): - logger.info("Moving %s pipeline radb_id=%s otdb_id=%s by %s from \'%s\' to \'%s\'", task['status'], task['id'], task['otdb_id'], shift, task['starttime'], newStartTime) - radbrpc.updateTaskAndResourceClaims(task['id'], starttime=newStartTime, endtime=newEndTime) + logger.info("Moving %s pipeline radb_id=%s otdb_id=%s by %s from \'%s\' to \'%s\'", task['status'], task['id'], task['otdb_id'], format_timedelta(shift), task['starttime'], newStartTime) + if not radbrpc.updateTaskAndResourceClaims(task['id'], starttime=newStartTime, endtime=newEndTime): + logger.warn("Could not update start/endtime for pipeline radb_id=%s otdb_id=%s", + updated_task['id'], updated_task['otdb_id']) + updated_task = radbrpc.getTask(task['id']) + if updated_task['status'] != task['status']: logger.warn("Moving of pipeline radb_id=%s otdb_id=%s caused the status to change from %s to %s", updated_task['id'], updated_task['otdb_id'], task['status'], updated_task['status']) #TODO: automatically resolve conflict status by moved pipeline in first free time slot. @@ -92,6 +101,8 @@ class ScheduleChecker(): mom_servicename=DEFAULT_MOMQUERY_SERVICENAME, cleanup_busname=DEFAULT_CLEANUP_BUSNAME, cleanup_servicename=DEFAULT_CLEANUP_SERVICENAME, + otdb_busname=DEFAULT_OTDB_SERVICE_BUSNAME, + otdb_servicename=DEFAULT_OTDB_SERVICENAME, broker=None): """ """ @@ -100,6 +111,7 @@ class ScheduleChecker(): self._radbrpc = RARPC(servicename=radb_servicename, busname=radb_busname, broker=broker) self._momrpc = MoMQueryRPC(servicename=mom_servicename, busname=mom_busname, broker=broker) self._curpc = CleanupRPC(busname=cleanup_busname, servicename=cleanup_servicename, broker=broker) + self._otdbrpc = OTDBRPC(busname=otdb_busname, servicename=otdb_servicename, broker=broker, timeout=180) def __enter__(self): """Internal use only. (handles scope 'with')""" @@ -115,6 +127,7 @@ class ScheduleChecker(): self._radbrpc.open() self._momrpc.open() self._curpc.open() + self._otdbrpc.open() self._running = True self._thread = Thread(target=self._check_loop) self._thread.daemon = True @@ -125,6 +138,7 @@ class ScheduleChecker(): self._radbrpc.close() self._momrpc.close() self._curpc.close() + self._otdbrpc.close() self._running = False self._thread.join(60) @@ -155,6 +169,12 @@ class ScheduleChecker(): try: now = datetime.utcnow() min_start_timestamp = now + timedelta(seconds=PIPELINE_CHECK_INTERVAL) + #round to next minute + min_start_timestamp = datetime(min_start_timestamp.year, + min_start_timestamp.month , + min_start_timestamp.day, + min_start_timestamp.hour, + min_start_timestamp.minute+1) pipelines = self._radbrpc.getTasks(task_status=['scheduled', 'queued'], task_type='pipeline', cluster='CEP4') @@ -180,8 +200,8 @@ class ScheduleChecker(): Here, we check for jobs that are yet to run, and are in "opened" in MoM. Such jobs are subsequently deleted from the RADB. """ try: - logger.info('checking approved tasks for status in mom') - unrun_tasks = self._radbrpc.getTasks(task_status=['approved', 'scheduled', 'prescheduled', 'queued', 'error'], task_type=['observation', 'pipeline']) + logger.info('checking unfinished tasks for status in mom') + unrun_tasks = self._radbrpc.getTasks(task_status=['approved', 'scheduled', 'prescheduled', 'queued', 'error', 'aborted'], task_type=['observation', 'pipeline']) mom_ids = [t['mom_id'] for t in unrun_tasks] mom_details = self._momrpc.getObjectDetails(mom_ids) @@ -220,11 +240,39 @@ class ScheduleChecker(): except Exception as e: logger.error("Error while checking unrun tasks for MoM opened/described/suspended status: %s", e) + def checkUnRunReservations(self): + """All non-mon tasks in otdb can be deleted in otdb without us knowing about it (there is no otdb-task-deleted event) + Here, we check for all non-mom tasks that are yet to run if they still exist in otdb. If not, such jobs are subsequently deleted from the RADB. + """ + try: + logger.info('checking reservation in otdb') + reservations = self._radbrpc.getTasks(task_type=['reservation'], lower_bound=datetime.utcnow()) + + for reservation in reservations: + try: + otdb_id = int(reservation['otdb_id']) + status = self._otdbrpc.taskGetStatus(otdb_id) + logger.info('reservation %s status = %s', otdb_id, status) + + if status != 'scheduled': + logger.info('deleting reservation otdb_id=%s radb_id=%s otdb_status=%s from rabd', + otdb_id, reservation['id'], status) + # delete the spec (and task/claims etc via cascading delete) from radb to get it in sync again with mom + self._radbrpc.deleteSpecification(reservation['specification_id']) + except Exception as e: + logger.error("Error while checking reservation otdb_id=%s radb_id=%s in otdb: %s", + reservation['otdb_id'], + reservation['id'], + e) + except Exception as e: + logger.error("Error while checking unrun tasks for MoM opened/described/suspended status: %s", e) + def _check_loop(self): while self._running: self.checkRunningPipelines() self.checkScheduledAndQueuedPipelines() self.checkUnRunTasksForMoMOpenedStatus() + self.checkUnRunReservations() for i in range(PIPELINE_CHECK_INTERVAL): sleep(1) diff --git a/SAS/ResourceAssignment/ResourceAssigner/lib/schedulers.py b/SAS/ResourceAssignment/ResourceAssigner/lib/schedulers.py index e37b80fbcbbd626859272970efa2bdc7eec51923..9e3e6e891a08b7fd7703b518527cc323fa5afd53 100644 --- a/SAS/ResourceAssignment/ResourceAssigner/lib/schedulers.py +++ b/SAS/ResourceAssignment/ResourceAssigner/lib/schedulers.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from lofar.common.cache import cache @@ -12,6 +12,9 @@ from lofar.sas.resourceassignment.resourceassigner.resource_availability_checker from lofar.mac.config import DEFAULT_OBSERVATION_CONTROL_BUS_NAME, DEFAULT_OBSERVATION_CONTROL_SERVICE_NAME from lofar.mac.observation_control_rpc import ObservationControlRPCClient +import logging +logger = logging.getLogger(__name__) + """ Scheduling is the process of looking for a suitable resource slot for a given task. @@ -33,6 +36,8 @@ from lofar.mac.observation_control_rpc import ObservationControlRPCClient class ScheduleException(Exception): + """ Scheduler related exceptions should distinctive from general Exceptions """ + def __init__(self, message): super(ScheduleException, self).__init__(message) @@ -68,6 +73,9 @@ class BasicScheduler(object): # Any resources that we cannot schedule on for some reason self.unusable_resources = [] + # Duration must be non-negative or weird stuff will happen + assert self.starttime <= self.endtime + def allocate_resources(self, estimates): """ Tries to allocate resources for the given estimates. @@ -96,7 +104,8 @@ class BasicScheduler(object): self.radb.commit() allocation_successful = True - except ScheduleException: + except ScheduleException, e: + logger.debug("BasicScheduler: scheduling threw exception: %s", e) self.radb.rollback() return allocation_successful @@ -109,7 +118,7 @@ class BasicScheduler(object): Does nothing in this base class. """ - pass + logger.debug("BasicScheduler: _pre_process_allocation for task %s", self.task_id) def _post_process_allocation(self): """ @@ -122,8 +131,10 @@ class BasicScheduler(object): :raises ScheduleException if the task at hand can't be set to the status "claimed" in RADB """ + logger.debug("BasicScheduler: _post_process_allocation for task %s", self.task_id) + # move all claims from tentative -> claimed - if not self.radb.updateResourceClaims(task_id=self.task_id, status="claimed", commit=False): + if not self.radb.updateResourceClaims(where_task_ids=[self.task_id], status="claimed", commit=False): raise ScheduleException("Failed to put tentative claims to claimed") def _get_resource_availability(self): @@ -136,7 +147,7 @@ class BasicScheduler(object): claimable_capacity_upper_bound=self.endtime) # resources WE claimed (note that their size is already accounted for in 'resources') - tentative_claims = self.radb.getResourceClaims(task_ids=self.task_id, status="tentative") + tentative_claims = self.radb.getResourceClaims(task_ids=[self.task_id], status="tentative", extended=True) tentative_resources = [c["resource_id"] for c in tentative_claims] # disable resources we cannot schedule on (we cannot schedule on resources with tentative @@ -168,54 +179,59 @@ class BasicScheduler(object): """ available_resources = self._get_resource_availability() + + logger.debug("Requested resources: %s", requested_resources) + logger.debug("Available resources: %s", available_resources) + try: tentative_claims = self.resource_availability_checker.get_is_claimable(requested_resources, available_resources) - except CouldNotFindClaimException: + logger.debug("Proposing tentative claims: %s", tentative_claims) + except CouldNotFindClaimException as e: + logger.error('_try_schedule CouldNotFindClaimException: %s', e) raise ScheduleException("Could not schedule") + assert tentative_claims + # add static info to all claims self._finalise_claims(tentative_claims) # insert all claims to reserve the resources in the next call to findfit and to find the conflicts according to # the DB claim_ids = self.radb.insertResourceClaims(self.task_id, tentative_claims, 'anonymous', -1, commit=False) + logger.debug("Resulting claim IDs: %s", claim_ids) + assert len(claim_ids) == len(tentative_claims), "Could not insert resource claims" - # tie the claim ids to the estimates - claim_to_estimates = {cid: tentative_claims[cid]["requested_resources"] for cid in claim_ids} + # tie the claim ids to the estimates. note that each "requested_resources" element is a list of claims + claim_to_estimates = {cid: tentative_claims[idx]["requested_resources"] for idx, cid in enumerate(claim_ids)} # try solving as much conflicts as possible. We need NOT resolve ALL conflicts: removing one conflict can free # up more resources as a by-product, in which case other conflicts can simply be shifted to those newly freed # resources. - conflict_claims = self.radb.getResourceClaims(task_ids=self.task_id, status="conflict") - if not any([self._resolve_conflict(c) for c in conflict_claims]): + conflict_claims = self.radb.getResourceClaims(task_ids=[self.task_id], status="conflict", extended=True) + logger.debug("Resulting claims in conflict before resolution: %s", conflict_claims) + if conflict_claims and not any([self._resolve_conflict(c) for c in conflict_claims]): # Could not resolve any conflict raise ScheduleException("Could not resolve any conflict") # remove conflicting claims (allowing the next iteration to propose alternatives). Note that _handle_conflicts # could have reduced the number of conflicting claims. - conflict_claims = self.radb.getResourceClaims(task_ids=self.task_id, status="conflict") + conflict_claims = self.radb.getResourceClaims(task_ids=[self.task_id], status="conflict", extended=True) + logger.debug("Resulting claims in conflict after resolution: %s", conflict_claims) + conflict_claim_ids = [c["id"] for c in conflict_claims] self.radb.deleteResourceClaims(conflict_claim_ids, commit=False) - # return any estimates that we could not fulfill - remaining_estimates = set().union([claim_to_estimates[cid] for cid in conflict_claim_ids]) - return remaining_estimates - - def _get_conflicting_claims_and_tasks(self, conflict_claim): - """ - Return all claims that are conflicting with our claim and all tasks associated with those claims. - - :param conflict_claim: our conflicting claim - - :returns 2-tuple (conflicting_claims, conflicting_tasks) - """ - - conflicting_claims = self.radb.get_conflicting_overlapping_claims(conflict_claim) - conflicting_task_ids = set([c["task_id"] for c in conflicting_claims]) - conflicting_tasks = self.radb.getTasks(task_ids=conflicting_task_ids) + # return any estimates that we could not fulfill. Note that requested_resources can contain the same item multiple + # times, so remove only the same number of estimates that were satisfied + satisfied_estimates = sum([claim_to_estimates[cid] for cid in claim_to_estimates if cid not in conflict_claim_ids],[]) + remaining_estimates = requested_resources[:] + for e in satisfied_estimates: + if e in remaining_estimates: + remaining_estimates.remove(e) - return conflicting_claims, conflicting_tasks + logger.debug("Remaining estimates: %s", remaining_estimates) + return remaining_estimates def _resolve_conflict(self, conflict_claim): """ Resolve one conflict, making it is useful to try to schedule again. @@ -259,6 +275,9 @@ class PriorityScheduler(BasicScheduler): broker=broker, timeout=120) + # Time needed in between tasks to setup the stations + self.STATION_SETUP_TIME_MINUTES = 1 + def open(self): """ Open connections to the required services """ self.momqueryservice.open() @@ -291,9 +310,14 @@ class PriorityScheduler(BasicScheduler): :returns the priority of the current task """ + logger.debug("my_task_priority, messing around with MoM QS") + my_momid = self.task["mom_id"] priority_dict = self.momqueryservice.get_project_priorities_for_objects([my_momid]) - return priority_dict[my_momid] + my_priority = priority_dict[my_momid] + + logger.debug("PriorityScheduler: my priority is %s", my_priority) + return my_priority def _kill_task_in_radb(self, task): """ @@ -302,7 +326,10 @@ class PriorityScheduler(BasicScheduler): :param task: task to 'set' to killed """ - self.radb.updateTaskAndResourceClaims(task_id=task['task_id'], endtime=datetime.utcnow(), commit=False) + logger.debug("kill_task_in_radb: task: %s", task) + + new_endtime = max(task['starttime'], datetime.utcnow()) + self.radb.updateTaskAndResourceClaims(task_id=task['id'], task_status='aborted', endtime=new_endtime, commit=False) def _propose_potential_starttime(self, newstarttime): """ @@ -311,6 +338,8 @@ class PriorityScheduler(BasicScheduler): :param newstarttime: the newly proposed start time """ + logger.debug("PriorityScheduler: Proposing starttime %s", newstarttime) + self.earliest_potential_starttime = min(self.earliest_potential_starttime, newstarttime) def _resolve_conflict(self, conflict_claim): @@ -322,6 +351,8 @@ class PriorityScheduler(BasicScheduler): :return: True if conflict resolution was effective, or False if not """ + logger.debug("resolve_conflicts: conflicting_claims: %s", conflict_claim) + # try to resolve the conflict, and mark any resources unavailable if resolution fails tasks_to_kill = self._get_resolvable_conflicting_tasks(conflict_claim) @@ -332,12 +363,33 @@ class PriorityScheduler(BasicScheduler): self._kill_task_in_radb(t) if not tasks_to_kill: + logger.debug("resolve_conflicts: no tasks to kill") + # record which resources cannot be used anymore, because we can't kill anything on it - self.unusable_resources.append(conflict_claim["resource"]) + self.unusable_resources.append(conflict_claim["resource_id"]) # Return True if we killed anything return tasks_to_kill != [] + def _get_conflicting_claims_and_tasks(self, conflict_claim): + """ + Return all claims that are conflicting with our claim and all tasks associated with those claims. + + :param conflict_claim: our conflicting claim + + :returns 2-tuple (conflicting_claims, conflicting_tasks) + """ + + logger.debug("get_conflicting_claims_and_tasks for task ID: %s", conflict_claim["task_id"]) + + conflicting_claims = self.radb.get_conflicting_overlapping_claims(conflict_claim["id"]) + conflicting_task_ids = set([c["task_id"] for c in conflicting_claims]) + conflicting_tasks = self.radb.getTasks(task_ids=conflicting_task_ids) + + assert self.task_id not in conflicting_task_ids, "Task %s in conflict with itself!" % self.task_id + + return conflicting_claims, conflicting_tasks + def _get_resolvable_conflicting_tasks(self, conflict_claim): """ Return the tasks that that have resource claims which conflict with the given resource claim and have a lower @@ -355,14 +407,26 @@ class PriorityScheduler(BasicScheduler): # find all conflicting claims & which tasks they belong to conflicting_claims, conflicting_tasks = self._get_conflicting_claims_and_tasks(conflict_claim) conflicting_task_momids = [t["mom_id"] for t in conflicting_tasks] + logger.debug("PriorityScheduler: conflicting claims are %s", conflicting_claims) + logger.debug("PriorityScheduler: conflicting tasks are %s", conflicting_tasks) # check which tasks we can kill task_priorities = self.momqueryservice.get_project_priorities_for_objects(conflicting_task_momids) + logger.debug("PriorityScheduler: conflicting task priorities are %s", task_priorities) kill_task_list = [t for t in conflicting_tasks if task_priorities[t["mom_id"]] < self._my_task_priority()] + logger.debug("PriorityScheduler: task kill list is %s", kill_task_list) # update if we're blocked by an earlier task than we know so far - earliest_potential_starttime = min([t["endtime"] for t in conflicting_tasks if t not in kill_task_list]) - self._propose_potential_starttime(earliest_potential_starttime) + unkillable_task_ids = [t["id"] for t in conflicting_tasks if t not in kill_task_list] + logger.debug("PriorityScheduler: unkillable task kill list is %s", unkillable_task_ids) + if unkillable_task_ids: + # note that we need to use the endtime of the claims, as they may extend beyond the task + earliest_potential_starttime = min([c["endtime"] for c in conflicting_claims if c["task_id"] in unkillable_task_ids]) + + # Allow the system X minutes station setup + earliest_potential_starttime += timedelta(minutes=self.STATION_SETUP_TIME_MINUTES) + + self._propose_potential_starttime(earliest_potential_starttime) return kill_task_list @@ -374,7 +438,9 @@ class PriorityScheduler(BasicScheduler): :raises ScheduleException if a task can't be killed because it's not an observation job """ - for t in set(tasks): + logger.debug("kill_tasks: tasks: %s", tasks) + + for t in tasks: if t["type"] == "observation": self.obscontrol.abort_observation(t["otdb_id"]) else: @@ -382,7 +448,6 @@ class PriorityScheduler(BasicScheduler): # the case for current pipelines raise ScheduleException("Cannot kill jobs of type %s yet" % t["type"]) - class DwellScheduler(PriorityScheduler): """ A Scheduler that searches for an allocation with a flexible start time. @@ -432,11 +497,16 @@ class DwellScheduler(PriorityScheduler): broker=broker ) - self.task_id = task_id self.min_starttime = min_starttime self.max_starttime = max_starttime self.duration = duration + # Duration must be non-negative or weird stuff will happen + assert self.duration >= timedelta(0, 0, 0) + + # Time span for starttime must be sane + assert self.min_starttime <= self.max_starttime + def _new_starttime(self, starttime): """ Set new start and end time based on the start time @@ -446,16 +516,20 @@ class DwellScheduler(PriorityScheduler): self.starttime = starttime self.endtime = starttime + self.duration + logger.debug("DwellScheduler: Trying %s - %s", self.starttime, self.endtime) + def _post_process_allocation(self): """ After resource scheduling, update the start and end times of the task at hand in the RADB. """ super(DwellScheduler, self)._post_process_allocation() - self.radb.updateTaskAndResourceClaims(task_id=self.task_id, - starttime=self.starttime, - endtime=self.endtime, - commit=False) + # Update the task start/endtime ONLY because the claims already were inserted with the right + # start/endtime. + self.radb.updateSpecification(self.task['specification_id'], + starttime=self.starttime, + endtime=self.endtime, + commit=False) def allocate_resources(self, resource_requests): """ @@ -468,19 +542,21 @@ class DwellScheduler(PriorityScheduler): """ self._new_starttime(self.min_starttime) - success = False - while not success and self.starttime <= self.max_starttime: + while True: # Find a solution success = super(DwellScheduler, self).allocate_resources(resource_requests) if success: - break + return True # Try the next slot new_starttime = self.earliest_potential_starttime - if new_starttime > self.starttime: + if new_starttime <= self.starttime: raise ScheduleException("DwellScheduler cannot advance") + if new_starttime > self.max_starttime: + logger.debug("DwellScheduler: Dwelled until the end. Earliest start time is %s but cannot go beyond %s.", new_starttime, self.max_starttime) + return False + self._new_starttime(new_starttime) - return success diff --git a/SAS/ResourceAssignment/ResourceAssigner/test/t_resource_availability_checker.py b/SAS/ResourceAssignment/ResourceAssigner/test/t_resource_availability_checker.py index 447e1f3c1d13c5e53ed10d92096627b1224e9d1a..5170f5275a3721ac4a0f366ddb3405d9c8fe4fdb 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/test/t_resource_availability_checker.py +++ b/SAS/ResourceAssignment/ResourceAssigner/test/t_resource_availability_checker.py @@ -1210,6 +1210,26 @@ class ResourceAvailabilityCheckerTest(unittest.TestCase): self.uut = ResourceAvailabilityChecker(self.rarpc_mock) + def test_disk_claim(self): + files = \ + { + 'uv': + [{ + 'identification': 'mom.G732487.B0.1.C.SAP000.uv.dps', + 'sap_nr': 0, + 'properties': + { + 'nr_of_uv_files': 120, + 'start_sb_nr': 0, + 'uv_file_size': 3617984960, + 'uv_otdb_id': 2 + } + }] + } + properties = self.uut._get_files_properties(files_dict = files, + io_type = 'input') + self.assertIsNotNone(properties) + def test_get_current_resource_usage(self): ''' Check if the resource availability checker returns a proper diff --git a/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py b/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py index 44f93c2fa7f70d66533484c2411bc23fe4bd7e12..85f2916162038eb1482cb80a2dcdb042744b339e 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py +++ b/SAS/ResourceAssignment/ResourceAssigner/test/t_resourceassigner.py @@ -146,6 +146,11 @@ class ResourceAssignerTest(unittest.TestCase): task_end_time = datetime.datetime(2016, 3, 25, 22, 47, 31) task_start_time = datetime.datetime(2016, 3, 25, 21, 47, 31) + task_minstarttime = task_start_time + task_maxendtime = task_start_time # + datetime.timedelta(hours=1) + task_minduration = 0 + task_maxduration = 0 + non_existing_task_mom_id = -1 predecessor_task_mom_id = 1 @@ -1548,6 +1553,12 @@ class ResourceAssignerTest(unittest.TestCase): self.momrpc_mock = momrpc_patcher.start() self.momrpc_mock.getPredecessorIds.return_value = {str(self.task_mom_id): self.predecessor_task_mom_ids} self.momrpc_mock.getSuccessorIds.return_value = {str(self.task_mom_id): self.successor_task_mom_ids} + self.momrpc_mock.get_time_restrictions.return_value = { + "minStartTime": self.task_minstarttime.strftime('%Y-%m-%dT%H:%M:%S'), + "maxEndTime": self.task_maxendtime.strftime('%Y-%m-%dT%H:%M:%S'), + "minDuration": str(self.task_minduration), + "maxDuration": str(self.task_maxduration) + } curpc_patcher = mock.patch('lofar.sas.datamanagement.cleanup.rpc') self.addCleanup(curpc_patcher.stop) @@ -2043,6 +2054,7 @@ class ResourceAssignerTest(unittest.TestCase): def test_do_assignment_updates_task_when_it_was_unable_to_claim_some_or_all_resources(self): self.dwell_scheduler_mock().allocate_resources.return_value = False + self.task["status"] = "conflict" self.resource_assigner.do_assignment(self.specification_tree['otdb_id'], self.specification_tree) @@ -2053,6 +2065,7 @@ class ResourceAssignerTest(unittest.TestCase): subject = 'Task' + 'Conflict' self.dwell_scheduler_mock().allocate_resources.return_value = False + self.task["status"] = "conflict" self.resource_assigner.do_assignment(self.specification_tree['otdb_id'], self.specification_tree) @@ -2063,6 +2076,7 @@ class ResourceAssignerTest(unittest.TestCase): subject = 'Task' + 'Conflict' self.dwell_scheduler_mock().allocate_resources.return_value = False + self.task["status"] = "conflict" self.resource_assigner.do_assignment(self.specification_tree['otdb_id'], self.specification_tree) diff --git a/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulechecker.py b/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulechecker.py index 8ef2c28b74b984cf695425a5781b29524c80b7a3..fdb3f3c7f9992458c176e3523c0e608b933663e4 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulechecker.py +++ b/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulechecker.py @@ -30,11 +30,12 @@ ra_notification_prefix = "ra_notification_prefix" class TestingScheduleChecker(ScheduleChecker): - def __init__(self, rarpc, momrpc, curpc): + def __init__(self, rarpc, momrpc, curpc, otdbrpc): # super gets not done to be able to insert mocks as early as possible otherwise the RPC block unittesting self._radbrpc = rarpc self._momrpc = momrpc self._curpc = curpc + self._otdbrpc = otdbrpc class ScheduleCheckerTest(unittest.TestCase): @@ -55,6 +56,10 @@ class ScheduleCheckerTest(unittest.TestCase): self.addCleanup(curpc_patcher.stop) self.curpc_mock = curpc_patcher.start() + otdbrpc_patcher = mock.patch('lofar.sas.otdb.otdbrpc.OTDBRPC') + self.addCleanup(otdbrpc_patcher.stop) + self.otdbrpc_mock = otdbrpc_patcher.start() + # Default return values self.rarpc_mock.getTasks.return_value = [ { 'id': 'id', 'mom_id': '1', 'otdb_id': 'otdb_id', 'status': 'approved', 'type': 'observation', 'specification_id': 'specification_id' } ] self.momrpc_mock.getObjectDetails.return_value = { 1: { 'object_status': 'approved' } } @@ -74,7 +79,7 @@ class ScheduleCheckerTest(unittest.TestCase): self.assertTrue(self.curpc_mock.close.called, "CURPC.close was not called") def test_contextManager_opens_and_closes_all_services(self): - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock): + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock): self.assert_all_services_opened() self.assert_all_services_closed() @@ -120,7 +125,7 @@ class ScheduleCheckerTest(unittest.TestCase): self.rarpc_mock.getTasks.return_value = [ { 'id': 'id', 'status': 'scheduled', 'type': 'pipeline', 'starttime': datetime.datetime.utcnow() } ] self.rarpc_mock.getTask.return_value = { 'id': 'id', 'status': 'scheduled', 'type': 'pipeline', 'starttime': datetime.datetime.utcnow() } - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock) as schedulechecker: + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock) as schedulechecker: schedulechecker.checkScheduledAndQueuedPipelines() self.assertTrue(movePipeline_mock.called, "Pipeline was not moved.") @@ -130,7 +135,7 @@ class ScheduleCheckerTest(unittest.TestCase): self.rarpc_mock.getTasks.return_value = [ { 'id': 'id', 'mom_id': '1', 'otdb_id': 'otdb_id', 'status': 'active', 'type': 'pipeline', 'specification_id': 'specification_id', 'endtime': datetime.datetime.utcnow() } ] - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock) as schedulechecker: + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock) as schedulechecker: schedulechecker.checkRunningPipelines() self.assertTrue(self.rarpc_mock.updateTaskAndResourceClaims.called, "Task was not updated.") @@ -140,7 +145,7 @@ class ScheduleCheckerTest(unittest.TestCase): """ Test if a MoM task on 'opened' and in OTDB on 'approved' causes the task to be deleted. """ self.momrpc_mock.getObjectDetails.return_value = { 1: { 'object_status': 'opened' } } - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock) as schedulechecker: + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock) as schedulechecker: schedulechecker.checkUnRunTasksForMoMOpenedStatus() self.assertTrue(self.curpc_mock.removeTaskData.called, "Task output was not deleted from disk") @@ -150,7 +155,7 @@ class ScheduleCheckerTest(unittest.TestCase): """ Test if a MoM task on 'approved' and in OTDB on 'approved' causes the task NOT to be deleted. """ self.momrpc_mock.getObjectDetails.return_value = { 1: { 'object_status': 'approved' } } - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock) as schedulechecker: + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock) as schedulechecker: schedulechecker.checkUnRunTasksForMoMOpenedStatus() self.assertFalse(self.curpc_mock.removeTaskData.called, "Task output was deleted from disk") @@ -160,7 +165,7 @@ class ScheduleCheckerTest(unittest.TestCase): """ Test if a task is not in MoM, and 'approved' in OTDB. This causes the task to be deleted. """ self.momrpc_mock.getObjectDetails.return_value = {} - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock) as schedulechecker: + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock) as schedulechecker: schedulechecker.checkUnRunTasksForMoMOpenedStatus() self.assertTrue(self.rarpc_mock.deleteSpecification.called, "Object was not removed from RADB") @@ -168,7 +173,7 @@ class ScheduleCheckerTest(unittest.TestCase): def test_checkUnRunTasksForMoMOpenedStatus_only_observations_pipelines(self): """ Test if only observations and pipelines are compared against the MoMDB (and not maintenance or reservation tasks, for example). """ - with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock) as schedulechecker: + with TestingScheduleChecker(self.rarpc_mock, self.momrpc_mock, self.curpc_mock, self.otdbrpc_mock) as schedulechecker: schedulechecker.checkUnRunTasksForMoMOpenedStatus() self.assertEquals(self.rarpc_mock.getTasks.call_args[1]["task_type"], ['observation', 'pipeline'], "Only observations and pipelines must be matched against MoMDB") diff --git a/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulers.py b/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulers.py index 3a8346bbd47a3a237f7e6d15ac29ce05f0e3014c..f25840546d68e9d517dba0aad8fe474c39518ae2 100755 --- a/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulers.py +++ b/SAS/ResourceAssignment/ResourceAssigner/test/t_schedulers.py @@ -23,1480 +23,517 @@ import unittest import mock import datetime +from copy import deepcopy + +from lofar.sas.resourceassignment.resourceassigner.resource_availability_checker import CouldNotFindClaimException from lofar.sas.resourceassignment.resourceassigner.schedulers import ScheduleException from lofar.sas.resourceassignment.resourceassigner.schedulers import BasicScheduler from lofar.sas.resourceassignment.resourceassigner.schedulers import PriorityScheduler from lofar.sas.resourceassignment.resourceassigner.schedulers import DwellScheduler +import logging +logger = logging.getLogger(__name__) -class BasicSchedulerTest(unittest.TestCase): - specification_id = 2323 - - task_mom_id = 351543 - task_otdb_id = 1290472 - task_id = 2299 - task_end_time = datetime.datetime(2016, 3, 25, 22, 47, 31) - task_start_time = datetime.datetime(2016, 3, 25, 21, 47, 31) - - def reset_task(self): - self.task = { - "mom_id": self.task_mom_id, - "otdb_id": self.task_otdb_id, - "id": self.task_id, - "endtime": self.task_end_time, - "name": "IS HBA_DUAL", - "predecessor_ids": [], - "project_mom_id": 2, - "project_name": "test-lofar", - "specification_id": self.specification_id, - "starttime": self.task_start_time, - "status": "prescheduled", - "status_id": 350, - "successor_ids": [], - "type": "pipeline", - "type_id": 0 - } +class FakeRADatabase(object): + """ Mimic an RA Database, assuming claims overlap fully or not at all. """ - def setUp(self): - """ Initialize mocks used by the tests """ - - self.reset_task() - - def get_task_side_effect(*args, **kwargs): - if 'mom_id' in kwargs: - if kwargs['mom_id'] == self.successor_task_mom_id: - return self.successor_task - elif kwargs['mom_id'] == self.predecessor_task_mom_id: - return self.predecessor_task - elif kwargs['mom_id'] == self.non_existing_task_mom_id: - return None - else: - return self.task - else: - return self.task - - # Mock out the ResourceAvailabilityChecker class - ra_avail_checker_patcher = mock.patch('lofar.sas.resourceassignment.resourceassigner' - '.resource_availability_checker.ResourceAvailabilityChecker') - self.addCleanup(ra_avail_checker_patcher.stop) - self.ra_avail_checker_mock = ra_avail_checker_patcher.start() - - # Mock out the RADatabase class used by the schedulers - ra_database_patcher = mock.patch('lofar.sas.resourceassignment.resourceassigner.schedulers.RADatabase') + def __init__(self, resource_capacity): + # database + self.tasks = {} + self.claims = {} + self.next_claim_id = 0 + + # cache committed state here + self.committed_tasks = {} + self.committed_claims = {} + + # maximum capacity of our resource + self.resource_capacity = resource_capacity + + def addTask(self, id, task): + self.tasks[id] = task + self.tasks[id]["id"] = id + self.tasks[id]["specification_id"] = id + self.claims[id] = [] + + self.committed = False + self.rolled_back = False + + def _fits(self, claim): + usage = 0 + resource_id = claim["resource_id"] + + for claims in self.claims.values(): + for c in claims: + overlap_in_time = claim["starttime"] < c["endtime"] and claim["endtime"] > c["starttime"] + overlap_in_resource = c["resource_id"] == resource_id + + if c["status"] != "conflict" and \ + c["id"] != claim.get("id",None) and \ + overlap_in_resource and \ + overlap_in_time: + usage += c["claim_size"] + + return usage + claim["claim_size"] <= self.resource_capacity + + """ Methods to mock radb. """ + + def getTask(self, id): + return self.tasks[id] + + def getTasks(self, task_ids): + return [t for id, t in self.tasks.iteritems() if id in task_ids] + + def updateSpecification(self, specification_id, starttime=None, endtime=None, content=None, cluster=None, + commit=True): + + for task_id, task in self.tasks.iteritems(): + if self.tasks[task_id]["specification_id"] == specification_id: + if starttime is not None: + self.tasks[task_id]["starttime"] = starttime + if endtime is not None: + self.tasks[task_id]["endtime"] = endtime + + return True + + def getResources(self, *args, **kwargs): + # we model two resources, can expand if needed + return [ { "id": 0 }, { "id": 1 } ] + + def getResourceClaims(self, task_ids, status, extended): + return [claim for tid in task_ids for claim in self.claims[tid] if claim["status"] == status] + + def deleteResourceClaims(self, claim_ids, commit): + logger.info("Deleting claims %s", claim_ids) + + for tid in self.claims: + self.claims[tid] = [c for c in self.claims[tid] if c["id"] not in claim_ids] + + def updateResourceClaims(self, where_task_ids, status, commit): + # this is what we support + assert status == "claimed" + + for task_id in where_task_ids: + # can't update conflict claims to claimed + for c in self.claims[task_id]: + if c["status"] != "tentative": + return False + + # update statusses + for c in self.claims[task_id]: + c["status"] = "claimed" + + return True + + def updateTaskAndResourceClaims(self, task_id, starttime=None, endtime=None, **kwargs): + if starttime: + logger.info("Setting starttime of task %s to %s", task_id, starttime) + + self.tasks[task_id]["starttime"] = starttime + + for c in self.claims[task_id]: + c["starttime"] = starttime - # TODO: should this be done globally? - # ra_database_patcher = mock.patch('lofar.sas.resourceassignment.database.radb.RADatabase') + if endtime: + logger.info("Setting endtime of task %s to %s", task_id, endtime) + self.tasks[task_id]["endtime"] = endtime + + for c in self.claims[task_id]: + c["endtime"] = endtime + + def insertResourceClaims(self, task_id, claims, *args, **kwargs): + for c in claims: + # check whether tasks do not get two claims of the same resource + assert c["resource_id"] not in [d["resource_id"] for d in self.claims[task_id]], "Resource %s claimed twice by task %s" % (c["resource_id"], task_id) + + # derive claim status + c["status"] = "tentative" if self._fits(c) else "conflict" + + # assign ids + c["task_id"] = task_id + c["id"] = self.next_claim_id + self.next_claim_id += 1 + + # add it to our claim list + self.claims[task_id].append(c) + + claim_ids = [c["id"] for c in claims] + logger.info("Added claims %s", claim_ids) + + return claim_ids + + def get_conflicting_overlapping_claims(self, claim_id): + overlapping_claims = [] + + logger.info('get_conflicting_overlapping_claims(claim_id=%s) self.claims content:', claim_id) + for claim_id, claim_value in self.claims.iteritems(): + logger.info('%s: %s', claim_id, claim_value) + + # all claims overlap + claims_for_id = self.claims[claim_id] + for claim in claims_for_id: + overlapping_claims += [c for _, claims in self.claims.iteritems() for c in claims if + # overlap in space + c["resource_id"] == claim["resource_id"] and + # "conflict" claims do not actually claim resources + c["status"] != "conflict" and + # be antireflexive + c["id"] != claim["id"]] + + return overlapping_claims + + def commit(self): + logger.info("Commit") + + self.committed = True + self.committed_claims = deepcopy(self.claims) + self.committed_tasks = deepcopy(self.tasks) + + def rollback(self): + logger.info("Rollback") + + self.rolled_back = True + self.claims = deepcopy(self.committed_claims) + self.tasks = deepcopy(self.committed_tasks) + +class FakeResourceAvailabilityChecker(object): + resource_types = { + "storage": 0, + "bandwidth": 1, + } + + def get_is_claimable(self, requested_resources, available_resources): + if not available_resources: + raise CouldNotFindClaimException + + # fullfil one request at a time to keep the code simple. We map it on + # the first available resource + r = requested_resources[0] + rtype = r["resource_types"].keys()[0] + return [{ + 'requested_resources': [r], + 'claim_size': r["resource_types"][rtype], + 'resource_id': available_resources[0]["id"], + 'resource_type_id': self.resource_types[rtype] + }] + +class SchedulerTest(unittest.TestCase): + """ Setup mechanics to use a FakeRADatabase and FakeResourceAvailabilityChecker to simulate a system with + one resource at one point in time. """ + + def mock_ra_database(self): + self.fake_ra_database = FakeRADatabase(resource_capacity=1024) + + ra_database_patcher = mock.patch('lofar.sas.resourceassignment.resourceassigner.schedulers.RADatabase') self.addCleanup(ra_database_patcher.stop) self.ra_database_mock = ra_database_patcher.start() + self.ra_database_mock.return_value = self.fake_ra_database - self.ra_database_mock.getTask.side_effect = get_task_side_effect - - self.ra_database_mock.insertSpecificationAndTask.return_value = { - 'inserted': True, - 'specification_id': self.specification_id, - 'task_id': self.task_id - } - - self.ra_database_mock.getResourceClaimPropertyTypes.return_value = [ - {'id': 0, 'name': 'nr_of_is_files'}, - {'id': 1, 'name': 'nr_of_cs_files'}, - {'id': 2, 'name': 'nr_of_uv_files'}, - {'id': 3, 'name': 'nr_of_im_files'}, - {'id': 4, 'name': 'nr_of_img_files'}, - {'id': 5, 'name': 'nr_of_pulp_files'}, - {'id': 6, 'name': 'nr_of_cs_stokes'}, - {'id': 7, 'name': 'nr_of_is_stokes'}, - {'id': 8, 'name': 'is_file_size'}, - {'id': 9, 'name': 'cs_file_size'}, - {'id': 10, 'name': 'uv_file_size'}, - {'id': 11, 'name': 'im_file_size'}, - {'id': 12, 'name': 'img_file_size'}, - {'id': 13, 'name': 'nr_of_pulp_files'}, - {'id': 14, 'name': 'nr_of_cs_parts'}, - {'id': 15, 'name': 'start_sb_nr'}, - {'id': 16, 'name': 'uv_otdb_id'}, - {'id': 17, 'name': 'cs_otdb_id'}, - {'id': 18, 'name': 'is_otdb_id'}, - {'id': 19, 'name': 'im_otdb_id'}, - {'id': 20, 'name': 'img_otdb_id'}, - {'id': 21, 'name': 'pulp_otdb_id'}, - {'id': 22, 'name': 'is_tab_nr'}, - {'id': 23, 'name': 'start_sbg_nr'}, - {'id': 24, 'name': 'pulp_file_size'} - ] - - self.ra_database_mock.getResourceTypes.return_value = [ - {'id': 0, 'name': 'rsp', 'unit_id': 0, 'units': 'rsp_channel_bit'}, - {'id': 1, 'name': 'tbb', 'unit_id': 1, 'units': 'bytes'}, - {'id': 2, 'name': 'rcu', 'unit_id': 2, 'units': 'rcu_board'}, - {'id': 3, 'name': 'bandwidth', 'unit_id': 3, 'units': 'bits/second'}, - {'id': 4, 'name': 'processor', 'unit_id': 4, 'units': 'cores'}, - {'id': 5, 'name': 'storage', 'unit_id': 1, 'units': 'bytes'}, - ] - - self.ra_database_mock.insertResourceClaims.return_value = {'ids': [1, 2]} - - self.ra_database_mock.getResourceGroupNames.return_value = [{"name": "CEP4"}, {"name": "DRAGNET"}, {"name": "COBALT"}] - - self.ra_database_mock.getResourceGroupMemberships.return_value = {'groups': [ - {'resource_group_parent_id': None, 'resource_group_parent_name': None, 'resource_group_id': 0, - 'resource_group_name': 'CORE', 'child_ids': [1, 2], 'parent_ids': [], 'resource_ids': [0, 1]}, - {'resource_group_parent_id': None, 'resource_group_parent_name': None, 'resource_group_id': 3, - 'resource_group_name': 'CS001', 'child_ids': [], 'parent_ids': [0], 'resource_ids': [212]}, - {'resource_group_parent_id': None, 'resource_group_parent_name': None, 'resource_group_id': 1, - 'resource_group_name': 'CEP4', 'child_ids': [], 'parent_ids': [0], 'resource_ids': [116, 117]}, - # {'resource_group_parent_id': None, 'resource_group_parent_name': None, 'resource_group_id': 4, # TODO: WHY DOES ORDER MATTER IN HERE??? - # 'resource_group_name': 'CS002', 'child_ids': [], 'parent_ids': [0], 'resource_ids': [214]}, # TODO: check what happens when this is moved after e.g. CS001; also comment in CS002 in RE response - ], - 'resources': [{'resource_group_parent_id': 0, - 'resource_group_parent_name': 'CORE', - 'resource_id': 0, - 'resource_name': 'CS001', - 'parent_group_ids': []}, - {'resource_group_parent_id': 0, - 'resource_group_parent_name': 'CORE', - 'resource_id': 1, - 'resource_name': 'CS002', - 'parent_group_ids': []}, - {'resource_group_parent_id': 1, - 'resource_group_parent_name': 'CEP4', - 'resource_id': 2, - 'resource_name': 'CEP4_storage:/data', - 'parent_group_ids': []}]} - - self.ra_database_mock.getResources.return_value = [ - {'id': 0, 'name': 'cpunode01_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 1, 'name': 'cpunode01_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 2, 'name': 'cpunode02_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 3, 'name': 'cpunode02_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 4, 'name': 'cpunode03_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 5, 'name': 'cpunode03_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 6, 'name': 'cpunode04_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 7, 'name': 'cpunode04_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 8, 'name': 'cpunode05_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 9, 'name': 'cpunode05_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 10, 'name': 'cpunode06_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 11, 'name': 'cpunode06_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 12, 'name': 'cpunode07_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 13, 'name': 'cpunode07_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 14, 'name': 'cpunode08_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 15, 'name': 'cpunode08_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 16, 'name': 'cpunode09_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 17, 'name': 'cpunode09_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 18, 'name': 'cpunode10_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 19, 'name': 'cpunode10_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 20, 'name': 'cpunode11_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 21, 'name': 'cpunode11_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 22, 'name': 'cpunode12_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 23, 'name': 'cpunode12_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 24, 'name': 'cpunode13_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 25, 'name': 'cpunode13_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 26, 'name': 'cpunode14_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 27, 'name': 'cpunode14_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 28, 'name': 'cpunode15_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 29, 'name': 'cpunode15_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 30, 'name': 'cpunode16_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 31, 'name': 'cpunode16_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 32, 'name': 'cpunode17_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 33, 'name': 'cpunode17_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 34, 'name': 'cpunode18_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 35, 'name': 'cpunode18_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 36, 'name': 'cpunode19_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 37, 'name': 'cpunode19_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 38, 'name': 'cpunode20_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 39, 'name': 'cpunode20_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 40, 'name': 'cpunode21_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 41, 'name': 'cpunode21_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 42, 'name': 'cpunode22_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 43, 'name': 'cpunode22_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 44, 'name': 'cpunode23_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 45, 'name': 'cpunode23_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 46, 'name': 'cpunode24_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 47, 'name': 'cpunode24_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 48, 'name': 'cpunode25_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 49, 'name': 'cpunode25_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 50, 'name': 'cpunode26_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 51, 'name': 'cpunode26_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 52, 'name': 'cpunode27_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 53, 'name': 'cpunode27_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 54, 'name': 'cpunode28_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 55, 'name': 'cpunode28_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 56, 'name': 'cpunode29_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 57, 'name': 'cpunode29_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 58, 'name': 'cpunode30_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 59, 'name': 'cpunode30_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 60, 'name': 'cpunode31_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 61, 'name': 'cpunode31_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 62, 'name': 'cpunode32_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 63, 'name': 'cpunode32_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 64, 'name': 'cpunode33_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 65, 'name': 'cpunode33_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 66, 'name': 'cpunode34_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 67, 'name': 'cpunode34_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 68, 'name': 'cpunode35_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 69, 'name': 'cpunode35_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 70, 'name': 'cpunode36_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 71, 'name': 'cpunode36_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 72, 'name': 'cpunode37_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 73, 'name': 'cpunode37_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 74, 'name': 'cpunode38_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 75, 'name': 'cpunode38_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 76, 'name': 'cpunode39_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 77, 'name': 'cpunode39_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 78, 'name': 'cpunode40_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 79, 'name': 'cpunode40_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 80, 'name': 'cpunode41_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 81, 'name': 'cpunode41_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 82, 'name': 'cpunode42_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 83, 'name': 'cpunode42_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 84, 'name': 'cpunode43_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 85, 'name': 'cpunode43_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 86, 'name': 'cpunode44_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 87, 'name': 'cpunode44_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 88, 'name': 'cpunode45_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 89, 'name': 'cpunode45_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 90, 'name': 'cpunode46_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 91, 'name': 'cpunode46_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 92, 'name': 'cpunode47_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 93, 'name': 'cpunode47_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 94, 'name': 'cpunode48_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 95, 'name': 'cpunode48_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 96, 'name': 'cpunode49_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 97, 'name': 'cpunode49_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 98, 'name': 'cpunode50_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 99, 'name': 'cpunode50_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 100, 'name': 'cbt001_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 101, 'name': 'cbt001_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 102, 'name': 'cbt002_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 103, 'name': 'cbt002_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 104, 'name': 'cbt003_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 105, 'name': 'cbt003_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 106, 'name': 'cbt004_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 107, 'name': 'cbt004_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 108, 'name': 'cbt005_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 109, 'name': 'cbt005_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 110, 'name': 'cbt006_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 111, 'name': 'cbt006_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 112, 'name': 'cbt007_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 113, 'name': 'cbt007_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 114, 'name': 'cbt008_bandwidth', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 115, 'name': 'cbt008_processors', 'type_id': 4, 'type_name': 'processor', 'unit_id': 4, - 'unit': 'cores', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 116, 'name': 'CEP4_bandwidth:/data', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 117, 'name': 'CEP4_storage:/data', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 118, 'name': 'dragproc_bandwidth:/data', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 119, 'name': 'dragproc_storage:/data', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 120, 'name': 'drg01_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 121, 'name': 'drg01_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 122, 'name': 'drg01_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 123, 'name': 'drg01_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 124, 'name': 'drg02_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 125, 'name': 'drg02_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 126, 'name': 'drg02_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 127, 'name': 'drg02_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 128, 'name': 'drg03_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 129, 'name': 'drg03_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 130, 'name': 'drg03_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 131, 'name': 'drg03_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 132, 'name': 'drg04_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 133, 'name': 'drg04_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 134, 'name': 'drg04_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 135, 'name': 'drg04_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 136, 'name': 'drg05_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 137, 'name': 'drg05_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 138, 'name': 'drg05_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 139, 'name': 'drg05_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 140, 'name': 'drg06_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 141, 'name': 'drg06_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 142, 'name': 'drg06_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 143, 'name': 'drg06_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 144, 'name': 'drg07_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 145, 'name': 'drg07_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 146, 'name': 'drg07_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 147, 'name': 'drg07_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 148, 'name': 'drg08_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 149, 'name': 'drg08_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 150, 'name': 'drg08_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 151, 'name': 'drg08_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 152, 'name': 'drg09_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 153, 'name': 'drg09_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 154, 'name': 'drg09_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 155, 'name': 'drg09_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 156, 'name': 'drg10_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 157, 'name': 'drg10_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 158, 'name': 'drg10_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 159, 'name': 'drg10_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 160, 'name': 'drg11_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 161, 'name': 'drg11_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 162, 'name': 'drg11_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 163, 'name': 'drg11_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 164, 'name': 'drg12_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 165, 'name': 'drg12_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 166, 'name': 'drg12_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 167, 'name': 'drg12_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 168, 'name': 'drg13_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 169, 'name': 'drg13_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 170, 'name': 'drg13_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 171, 'name': 'drg13_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 172, 'name': 'drg14_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 173, 'name': 'drg14_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 174, 'name': 'drg14_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 175, 'name': 'drg14_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 176, 'name': 'drg15_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 177, 'name': 'drg15_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 178, 'name': 'drg15_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 179, 'name': 'drg15_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 180, 'name': 'drg16_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 181, 'name': 'drg16_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 182, 'name': 'drg16_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 183, 'name': 'drg16_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 184, 'name': 'drg17_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 185, 'name': 'drg17_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 186, 'name': 'drg17_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 187, 'name': 'drg17_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 188, 'name': 'drg18_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 189, 'name': 'drg18_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 190, 'name': 'drg18_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 191, 'name': 'drg18_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 192, 'name': 'drg19_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 193, 'name': 'drg19_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 194, 'name': 'drg19_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 195, 'name': 'drg19_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 196, 'name': 'drg20_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 197, 'name': 'drg20_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 198, 'name': 'drg20_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 199, 'name': 'drg20_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 200, 'name': 'drg21_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 201, 'name': 'drg21_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 202, 'name': 'drg21_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 203, 'name': 'drg21_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 204, 'name': 'drg22_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 205, 'name': 'drg22_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 206, 'name': 'drg22_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 207, 'name': 'drg22_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 208, 'name': 'drg23_bandwidth:/data1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 209, 'name': 'drg23_bandwidth:/data2', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, - 'active': 1}, - {'id': 210, 'name': 'drg23_storage:/data1', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 211, 'name': 'drg23_storage:/data2', 'type_id': 5, 'type_name': 'storage', 'unit_id': 1, - 'unit': 'bytes', 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 212, 'name': 'CS001rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 213, 'name': 'CS001tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 214, 'name': 'CS002rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 215, 'name': 'CS002tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 216, 'name': 'CS003rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 217, 'name': 'CS003tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 218, 'name': 'CS004rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 219, 'name': 'CS004tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 220, 'name': 'CS005rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 221, 'name': 'CS005tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 222, 'name': 'CS006rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 223, 'name': 'CS006tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 224, 'name': 'CS007rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 225, 'name': 'CS007tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 226, 'name': 'CS011rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 227, 'name': 'CS011tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 228, 'name': 'CS013rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 229, 'name': 'CS013tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 230, 'name': 'CS017rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 231, 'name': 'CS017tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 232, 'name': 'CS021rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 233, 'name': 'CS021tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 234, 'name': 'CS024rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 235, 'name': 'CS024tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 236, 'name': 'CS026rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 237, 'name': 'CS026tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 238, 'name': 'CS028rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 239, 'name': 'CS028tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 240, 'name': 'CS030rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 241, 'name': 'CS030tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 242, 'name': 'CS031rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 243, 'name': 'CS031tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 244, 'name': 'CS032rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 245, 'name': 'CS032tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 246, 'name': 'CS101rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 247, 'name': 'CS101tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 248, 'name': 'CS103rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 249, 'name': 'CS103tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 250, 'name': 'CS201rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 251, 'name': 'CS201tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 252, 'name': 'CS301rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 253, 'name': 'CS301tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 254, 'name': 'CS302rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 255, 'name': 'CS302tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 256, 'name': 'CS401rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 257, 'name': 'CS401tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 258, 'name': 'CS501rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 259, 'name': 'CS501tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 260, 'name': 'RS106rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 261, 'name': 'RS106tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 262, 'name': 'RS205rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 263, 'name': 'RS205tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 264, 'name': 'RS208rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 265, 'name': 'RS208tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 266, 'name': 'RS210rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 267, 'name': 'RS210tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 268, 'name': 'RS305rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 269, 'name': 'RS305tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 270, 'name': 'RS306rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 271, 'name': 'RS306tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 272, 'name': 'RS307rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 273, 'name': 'RS307tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 274, 'name': 'RS310rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 275, 'name': 'RS310tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 276, 'name': 'RS406rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 277, 'name': 'RS406tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 278, 'name': 'RS407rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 279, 'name': 'RS407tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 280, 'name': 'RS408rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 281, 'name': 'RS408tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 282, 'name': 'RS409rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 283, 'name': 'RS409tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 284, 'name': 'RS503rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 285, 'name': 'RS503tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 286, 'name': 'RS508rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 287, 'name': 'RS508tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 288, 'name': 'RS509rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 289, 'name': 'RS509tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 290, 'name': 'DE601rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 291, 'name': 'DE601tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 292, 'name': 'DE602rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 293, 'name': 'DE602tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 294, 'name': 'DE603rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 295, 'name': 'DE603tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 296, 'name': 'DE604rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 297, 'name': 'DE604tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 298, 'name': 'DE605rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 299, 'name': 'DE605tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 300, 'name': 'FR606rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 301, 'name': 'FR606tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 302, 'name': 'SE607rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 303, 'name': 'SE607tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 304, 'name': 'UK608rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 305, 'name': 'UK608tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 306, 'name': 'DE609rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 307, 'name': 'DE609tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 308, 'name': 'PL610rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 309, 'name': 'PL610tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 310, 'name': 'PL611rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 311, 'name': 'PL611tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 312, 'name': 'PL612rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 313, 'name': 'PL612tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 314, 'name': 'IE613rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 315, 'name': 'IE613tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 316, 'name': 'IS614rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 317, 'name': 'IS614tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 318, 'name': 'TEST1rcu', 'type_id': 2, 'type_name': 'rcu', 'unit_id': 2, 'unit': 'rcu_board', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 319, 'name': 'TEST1tbb', 'type_id': 1, 'type_name': 'tbb', 'unit_id': 1, 'unit': 'bytes', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 320, 'name': 'CS001chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 321, 'name': 'CS001bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 322, 'name': 'CS001chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 323, 'name': 'CS001bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 324, 'name': 'CS002chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 325, 'name': 'CS002bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 326, 'name': 'CS002chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 327, 'name': 'CS002bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 328, 'name': 'CS003chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 329, 'name': 'CS003bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 330, 'name': 'CS003chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 331, 'name': 'CS003bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 332, 'name': 'CS004chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 333, 'name': 'CS004bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 334, 'name': 'CS004chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 335, 'name': 'CS004bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 336, 'name': 'CS005chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 337, 'name': 'CS005bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 338, 'name': 'CS005chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 339, 'name': 'CS005bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 340, 'name': 'CS006chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 341, 'name': 'CS006bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 342, 'name': 'CS006chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 343, 'name': 'CS006bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 344, 'name': 'CS007chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 345, 'name': 'CS007bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 346, 'name': 'CS007chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 347, 'name': 'CS007bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 348, 'name': 'CS011chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 349, 'name': 'CS011bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 350, 'name': 'CS011chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 351, 'name': 'CS011bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 352, 'name': 'CS013chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 353, 'name': 'CS013bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 354, 'name': 'CS013chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 355, 'name': 'CS013bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 356, 'name': 'CS017chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 357, 'name': 'CS017bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 358, 'name': 'CS017chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 359, 'name': 'CS017bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 360, 'name': 'CS021chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 361, 'name': 'CS021bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 362, 'name': 'CS021chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 363, 'name': 'CS021bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 364, 'name': 'CS024chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 365, 'name': 'CS024bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 366, 'name': 'CS024chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 367, 'name': 'CS024bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 368, 'name': 'CS026chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 369, 'name': 'CS026bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 370, 'name': 'CS026chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 371, 'name': 'CS026bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 372, 'name': 'CS028chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 373, 'name': 'CS028bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 374, 'name': 'CS028chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 375, 'name': 'CS028bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 376, 'name': 'CS030chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 377, 'name': 'CS030bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 378, 'name': 'CS030chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 379, 'name': 'CS030bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 380, 'name': 'CS031chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 381, 'name': 'CS031bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 382, 'name': 'CS031chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 383, 'name': 'CS031bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 384, 'name': 'CS032chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 385, 'name': 'CS032bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 386, 'name': 'CS032chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 387, 'name': 'CS032bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 388, 'name': 'CS101chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 389, 'name': 'CS101bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 390, 'name': 'CS101chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 391, 'name': 'CS101bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 392, 'name': 'CS103chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 393, 'name': 'CS103bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 394, 'name': 'CS103chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 395, 'name': 'CS103bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 396, 'name': 'CS201chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 397, 'name': 'CS201bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 398, 'name': 'CS201chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 399, 'name': 'CS201bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 400, 'name': 'CS301chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 401, 'name': 'CS301bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 402, 'name': 'CS301chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 403, 'name': 'CS301bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 404, 'name': 'CS302chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 405, 'name': 'CS302bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 406, 'name': 'CS302chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 407, 'name': 'CS302bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 408, 'name': 'CS401chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 409, 'name': 'CS401bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 410, 'name': 'CS401chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 411, 'name': 'CS401bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 412, 'name': 'CS501chan0', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 413, 'name': 'CS501bw0', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 414, 'name': 'CS501chan1', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 415, 'name': 'CS501bw1', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 416, 'name': 'RS106chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 417, 'name': 'RS106bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 418, 'name': 'RS205chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 419, 'name': 'RS205bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 420, 'name': 'RS208chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 421, 'name': 'RS208bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 422, 'name': 'RS210chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 423, 'name': 'RS210bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 424, 'name': 'RS305chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 425, 'name': 'RS305bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 426, 'name': 'RS306chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 427, 'name': 'RS306bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 428, 'name': 'RS307chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 429, 'name': 'RS307bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 430, 'name': 'RS310chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 431, 'name': 'RS310bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 432, 'name': 'RS406chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 433, 'name': 'RS406bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 434, 'name': 'RS407chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 435, 'name': 'RS407bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 436, 'name': 'RS408chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 437, 'name': 'RS408bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 438, 'name': 'RS409chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 439, 'name': 'RS409bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 440, 'name': 'RS503chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 441, 'name': 'RS503bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 442, 'name': 'RS508chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 443, 'name': 'RS508bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 444, 'name': 'RS509chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 445, 'name': 'RS509bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 446, 'name': 'DE601chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 447, 'name': 'DE601bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 448, 'name': 'DE602chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 449, 'name': 'DE602bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 450, 'name': 'DE603chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 451, 'name': 'DE603bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 452, 'name': 'DE604chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 453, 'name': 'DE604bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 454, 'name': 'DE605chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 455, 'name': 'DE605bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 456, 'name': 'FR606chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 457, 'name': 'FR606bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 458, 'name': 'SE607chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 459, 'name': 'SE607bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 460, 'name': 'UK608chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 461, 'name': 'UK608bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 462, 'name': 'DE609chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 463, 'name': 'DE609bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 464, 'name': 'PL610chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 465, 'name': 'PL610bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 466, 'name': 'PL611chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 467, 'name': 'PL611bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 468, 'name': 'PL612chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 469, 'name': 'PL612bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 470, 'name': 'IE613chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 471, 'name': 'IE613bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 472, 'name': 'IS614chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 473, 'name': 'IS614bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 474, 'name': 'TEST1chan', 'type_id': 0, 'type_name': 'rsp', 'unit_id': 0, - 'unit': 'rsp_channel_bit', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1}, - {'id': 475, 'name': 'TEST1bw', 'type_id': 3, 'type_name': 'bandwidth', 'unit_id': 3, - 'unit': 'bits/second', - 'available_capacity': 10, 'used_capacity': 0, 'total_capacity': 10, 'active': 1} - ] - - self.ra_database_mock.getResourceClaims.return_value = [] - - self.ra_database_mock.getResourceAllocationConfig.return_value = [ - {'name': 'max_fill_ratio_CEP4_storage', 'value': 0.85}, {'name': 'claim_timeout', 'value': 172800}, - {'name': 'min_inter_task_delay', 'value': 60}, {'name': 'max_fill_ratio_CEP4_bandwidth', 'value': 0.75} - ] - - def test_create_BasicScheduler_with_invalid_task_id_raises_exception(self): - with self.assertRaises(AssertionError): - BasicScheduler(None, self.ra_avail_checker_mock) + def mock_resource_availability_checker(self): + self.fake_resource_availability_checker = FakeResourceAvailabilityChecker() - # TODO: make this test work - def test_allocate_resources_unassignable_estimates_results_in_radb_rollback(self): - uut = BasicScheduler(1, self.ra_avail_checker_mock) - estimates = { - 'estimates': [{ - 'resource_types': {'bandwidth': 2, 'storage': 2}, - 'resource_count': 2, 'root_resource_group': 'CEP10', # <-- storage 'CEP10' is unsupported - 'output_files': { - 'uv': [{'sap_nr': 0, 'identifications': [], - 'properties': {'uv_file_size': 1073741824, 'nr_of_uv_files': 1, 'start_sb_nr': 0} - }] - } - }] - } + def setUp(self): + self.mock_ra_database() + self.mock_resource_availability_checker() - allocation_successful = uut.allocate_resources(estimates) +class BasicSchedulerTest(SchedulerTest): + def new_task(self, task_id): + self.fake_ra_database.addTask(task_id, { + "starttime": datetime.datetime(2017, 1, 1, 1, 0, 0), + "endtime": datetime.datetime(2017, 1, 1, 2, 0, 0), + }) + + self.scheduler = BasicScheduler(task_id, self.fake_resource_availability_checker, None) - self.assertFalse(allocation_successful) - # TODO: find out why this doesn't work! - self.ra_database_mock.rollback.assert_any_call() + def test_schedule_task(self): + """ Whether a task (that fits) can be scheduled. """ - # TODO: make this test work - def test_allocate_resources_assignable_estimates_results_in_radb_commit(self): - uut = BasicScheduler(1, self.ra_avail_checker_mock) - uut.radb = self.ra_database_mock + # Resources we need + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) - estimates = { - 'estimates': [{ - 'resource_types': {'bandwidth': 2, 'storage': 2}, - 'resource_count': 2, 'root_resource_group': 'CEP4', - 'output_files': { - 'uv': [{'sap_nr': 0, 'identifications': [], - 'properties': {'uv_file_size': 1073741824, 'nr_of_uv_files': 1, 'start_sb_nr': 0} - }] - } - }] - } + # Allocation must succeed and be committed + self.assertTrue(allocation_succesful) + self.assertTrue(self.fake_ra_database.committed) + self.assertFalse(self.fake_ra_database.rolled_back) - allocation_successful = uut.allocate_resources(estimates) + # Claim must be present in database + claims = self.fake_ra_database.claims[0] + self.assertTrue(claims) + self.assertEqual(len(claims), 1) - self.assertTrue(allocation_successful) + # Claim must be valid + claim = claims[0] + task = self.fake_ra_database.tasks[0] - # TODO: find out why this doesn't work! - self.ra_database_mock.commit.assert_any_call() + self.assertEqual(claim["status"], "claimed") + self.assertEqual(claim["starttime"], task["starttime"]) + self.assertEqual(claim["endtime"], task["endtime"]) + self.assertEqual(claim["claim_size"], 512) + self.assertEqual(claim["resource_type_id"], FakeResourceAvailabilityChecker.resource_types["bandwidth"]) - def test_post_process_allocation_raises_if_updating_status_fails(self): - uut = BasicScheduler(1, self.ra_avail_checker_mock) - uut.radb = self.ra_database_mock - self.ra_database_mock.updateResourceClaims.return_value = False + def test_multiple_resources(self): + """ Whether a task (that fits) can be scheduled. """ - with self.assertRaises(ScheduleException): - uut._post_process_allocation() + # Resources we need + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }, + { 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) - # TODO: implement test - def test_get_resource_availability_no_resources_available_returns_empty_list(self): - self.assertTrue(False) + # Allocation must succeed + self.assertTrue(allocation_succesful) - # TODO: implement test - def test_get_resource_availability_normal_use_succeeds(self): - self.assertTrue(False) + def test_schedule_too_large_task(self): + """ Whether a task with too large claims will be rejected by the scheduler. """ - # TODO: implement test - def test_finalise_claims_normal_use_succeeds(self): - self.assertTrue(False) + # Resources we need + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 2048} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) - # TODO: implement test - def test_try_schedule_raises_when_claim_requests_are_unclaimable(self): - self.assertTrue(False) + # Allocation must fail, and rollback() called + self.assertFalse(allocation_succesful) + self.assertFalse(self.fake_ra_database.committed) + self.assertTrue(self.fake_ra_database.rolled_back) - # TODO: implement test - def test_try_schedule_raises_when_no_conflicts_are_solved(self): - self.assertTrue(False) + def test_schedule_two_tasks_too_large_task(self): + """ Whether two tasks that fit individually but not together will be rejected by the scheduler. """ - # TODO: implement test - def test_try_schedule_returns_remaining_estimates(self): - self.assertTrue(False) + # First task must succeed + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) - # TODO: implement test - def test_get_conflicting_claims_and_tasks_normal_use_succeeds(self): - self.assertTrue(False) + # Second task must fail + self.new_task(1) + estimates = [{ 'resource_types': {'bandwidth': 513} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertFalse(allocation_succesful) - def test_resolve_conflict_returns_false(self): - """ Verify that BasicScheduler._resolve returns False, since it is not intended to resolve conflicts """ +class PrioritySchedulerTest(BasicSchedulerTest): + # The PriorityScheduler must not regress on the BasicScheduler, so we inherit all its tests - uut = BasicScheduler(1, self.ra_avail_checker_mock) + def mock_momrpc(self): + class FakeMoMQueryService(object): + def get_project_priorities_for_objects(self, mom_ids): + # priority increments by 1000 ids + return {mom_id: mom_id/1000 for mom_id in mom_ids} - result = uut._resolve_conflict(None) + self.fake_momrpc = FakeMoMQueryService() - self.assertFalse(result) + momrpc_patcher = mock.patch('lofar.sas.resourceassignment.resourceassigner.schedulers.MoMQueryRPC') + self.addCleanup(momrpc_patcher.stop) + self.momrpc_mock = momrpc_patcher.start() + self.momrpc_mock.return_value = self.fake_momrpc + def mock_obscontrol(self): + obscontrol_patcher = mock.patch('lofar.sas.resourceassignment.resourceassigner.schedulers.ObservationControlRPCClient.abort_observation') + self.addCleanup(obscontrol_patcher.stop) + self.obscontrol_mock = obscontrol_patcher.start() -# TODO: implement test -class PrioritySchedulerTest(unittest.TestCase): - def test_x(self): - pass + def mock_datetime(self): + datetime_patcher = mock.patch('lofar.sas.resourceassignment.resourceassigner.schedulers.datetime') + self.addCleanup(datetime_patcher.stop) + datetime_mock = datetime_patcher.start() + datetime_mock.utcnow.return_value = datetime.datetime(2017, 1, 1, 0, 0, 0) + datetime_mock.max = datetime.datetime.max -# TODO: implement test -class DwellSchedulerTest(unittest.TestCase): - def test_x(self): - pass + def setUp(self): + super(PrioritySchedulerTest, self).setUp() + + self.mock_momrpc() + self.mock_obscontrol() + self.mock_datetime() + + def new_task(self, task_id): + self.fake_ra_database.addTask(task_id, { + "mom_id": 1000 + task_id, + "otdb_id": 2000 + task_id, + "type": "observation", + "starttime": datetime.datetime(2017, 1, 1, 1, 0, 0), + "endtime": datetime.datetime(2017, 1, 1, 2, 0, 0), + }) + + self.scheduler = PriorityScheduler(task_id, self.fake_resource_availability_checker, None) + + def test_kill_lower_priority(self): + """ + Whether two tasks that fit individually but not together will be accepted by the scheduler by killing the + lower-priority task. + """ + + # First task must succeed + self.new_task(0) + estimates = [{'resource_types': {'bandwidth': 512}}] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must succeed as it has a higher priority + self.new_task(1000) + estimates = [{'resource_types': {'bandwidth': 513}}] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # First task must have been killed + otdb_id = self.fake_ra_database.tasks[0]["otdb_id"] + self.obscontrol_mock.assert_called_with(otdb_id) + + # First task must have its endtime cut short to utcnow or starttime + my_starttime = self.fake_ra_database.tasks[1000]["starttime"] + for c in self.fake_ra_database.claims[0]: + self.assertLessEqual(c["endtime"], my_starttime) + + def test_not_kill_higher_priority(self): + """ Whether two tasks that fit individually but not together get rejected priorities do not allow an override. """ + + # First task must succeed + self.new_task(1000) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must fail as it has a lower priority + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 513} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertFalse(allocation_succesful) + + # First task must NOT have been killed + otdb_id = self.fake_ra_database.tasks[1000]["otdb_id"] + with self.assertRaises(AssertionError): + self.obscontrol_mock.assert_called_with(otdb_id) + + def test_not_kill_equal_priority(self): + """ Whether two tasks that fit individually but not together get rejected priorities do not allow an override. """ + + # First task must succeed + self.new_task(1) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must fail as it has a lower priority + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 513} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertFalse(allocation_succesful) + + def test_partial_conflict(self): + """ Whether a task gets scheduled correctly if it has a partial conflict after the first fit. """ + + # First task must succeed + self.new_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }, + { 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must succeed as it has a higher priority + self.new_task(1000) + estimates = [{ 'resource_types': {'bandwidth': 512} }, + { 'resource_types': {'bandwidth': 513} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # First task must have been killed + otdb_id = self.fake_ra_database.tasks[0]["otdb_id"] + self.obscontrol_mock.assert_called_with(otdb_id) + +class DwellSchedulerTest(PrioritySchedulerTest): + # The DwellScheduler must not regress on the PriorityScheduler, so we inherit all its tests + + def new_task(self, task_id): + self.fake_ra_database.addTask(task_id, { + "mom_id": 1000 + task_id, + "otdb_id": 2000 + task_id, + "type": "observation", + "starttime": datetime.datetime(2017, 1, 1, 1, 0, 0), + "endtime": datetime.datetime(2017, 1, 1, 2, 0, 0), + }) + + self.fake_ra_database.commit() + self.fake_ra_database.committed = False # dont confuse subsequent checks on whether the scheduler committed + + self.scheduler = DwellScheduler(task_id, + datetime.datetime(2017, 1, 1, 1, 0, 0), # minstarttime + datetime.datetime(2017, 1, 1, 1, 0, 0), # maxstarttime + datetime.timedelta(hours=1), # duration + self.fake_resource_availability_checker, None) + + def new_dwell_task(self, task_id): + self.new_task(task_id) + + self.scheduler = DwellScheduler(task_id, + datetime.datetime(2017, 1, 1, 1, 0, 0), # minstarttime + datetime.datetime(2017, 1, 2, 1, 0, 0), # maxstarttime + datetime.timedelta(hours=1), # duration + self.fake_resource_availability_checker, None) + + def test_no_dwell(self): + """ Whether a task will not dwell unnecessarily on an empty system. """ + + # Task must succeed + self.new_dwell_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Task must NOT have been moved + self.assertEqual(self.fake_ra_database.tasks[0]["starttime"], datetime.datetime(2017, 1, 1, 1, 0, 0)) + + def test_dwell(self): + """ Whether a task will dwell after an existing task. """ + + # First task must succeed + self.new_dwell_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must also succeed + self.new_dwell_task(1) + estimates = [{ 'resource_types': {'bandwidth': 513} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must have been moved, first task not + self.assertEqual(self.fake_ra_database.tasks[0]["starttime"], datetime.datetime(2017, 1, 1, 1, 0, 0)) + self.assertEqual(self.fake_ra_database.tasks[0]["endtime"], datetime.datetime(2017, 1, 1, 2, 0, 0)) + self.assertEqual(self.fake_ra_database.tasks[1]["starttime"], datetime.datetime(2017, 1, 1, 2, 1, 0)) + self.assertEqual(self.fake_ra_database.tasks[1]["endtime"], datetime.datetime(2017, 1, 1, 3, 1, 0)) + + def test_dwell_respect_claim_endtime(self): + """ Whether a dwelling task will honour the claim endtimes, instead of the task endtime. """ + + # First task must succeed + self.new_dwell_task(0) + estimates = [{ 'resource_types': {'bandwidth': 512} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Extend claim + self.fake_ra_database.claims[0][0]["endtime"] += datetime.timedelta(hours=1) + + # Second task must also succeed + self.new_dwell_task(1) + estimates = [{ 'resource_types': {'bandwidth': 513} }] + allocation_succesful = self.scheduler.allocate_resources(estimates) + self.assertTrue(allocation_succesful) + + # Second task must have been moved beyond claim endtime + self.assertEqual(self.fake_ra_database.tasks[1]["starttime"], datetime.datetime(2017, 1, 1, 3, 1, 0)) + self.assertEqual(self.fake_ra_database.tasks[1]["endtime"], datetime.datetime(2017, 1, 1, 4, 1, 0)) if __name__ == '__main__': + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.DEBUG) + unittest.main() + diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py index a4286dc5cfc33900a5a4cd11f2f5f7ab48813245..5ee4e2b56646165d64ad9626f0ecae1dc244b528 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb.py @@ -45,8 +45,10 @@ class RADatabase: self.conn = None self.cursor = None self.log_queries = log_queries - self._taskStatusCache = {} - self._taskTypeCache = {} + self._taskStatusName2IdCache = {} + self._taskTypeName2IdCache = {} + self._claimStatusName2IdCache = {} + self._claimStatusId2NameCache = {} def _connect(self): self.conn = None @@ -69,13 +71,18 @@ class RADatabase: def _executeQuery(self, query, qargs=None, fetch=_FETCH_NONE): ''' Execute the query and reconnect upon OperationalError ''' - if self.log_queries: - logger.info('executing query: %s' % self._queryAsSingleLine(query, qargs)) - # Allow for 5 connection retries for i in range(5): try: + start = datetime.utcnow() self.cursor.execute(query, qargs) + if self.log_queries: + elapsed = datetime.utcnow() - start + elapsed_ms = 1000.0 * totalSeconds(elapsed) + logger.info('executed query in %.1fms%s yielding %s rows: %s', elapsed_ms, + ' (SLOW!)' if elapsed_ms > 250 else '', # for easy log grep'ing + self.cursor.rowcount, + self._queryAsSingleLine(query, qargs)) break except (psycopg2.OperationalError, AttributeError) as e: if isinstance(e, psycopg2.OperationalError): @@ -87,14 +94,12 @@ class RADatabase: logger.info("connected to radb") time.sleep(i*i) except (psycopg2.IntegrityError, psycopg2.ProgrammingError, psycopg2.InternalError, psycopg2.DataError)as e: + self._log_database_notifications() logger.error("Rolling back query=\'%s\' due to error: \'%s\'" % (self._queryAsSingleLine(query, qargs), e)) self.rollback() return [] - if self.conn.notices: - for notice in self.conn.notices: - logger.info('database log message %s', notice.strip()) - del self.conn.notices[:] + self._log_database_notifications() if fetch == _FETCH_ONE: return self.cursor.fetchone() @@ -102,6 +107,11 @@ class RADatabase: if fetch == _FETCH_ALL: return self.cursor.fetchall() + def _log_database_notifications(self): + if self.conn.notices: + for notice in self.conn.notices: + logger.info('database log message %s', notice.strip()) + del self.conn.notices[:] def commit(self): logger.info('commit') @@ -119,16 +129,16 @@ class RADatabase: def getTaskStatusNames(self): return [x['name'] for x in self.getTaskStatuses()] - def getTaskStatusId(self, status_name, from_cache=False): - if from_cache and status_name in self._taskStatusCache: - return self._taskStatusCache[status_name] + def getTaskStatusId(self, status_name, from_cache=True): + if from_cache and status_name in self._taskStatusName2IdCache: + return self._taskStatusName2IdCache[status_name] query = '''SELECT id from resource_allocation.task_status WHERE name = %s;''' result = self._executeQuery(query, [status_name], fetch=_FETCH_ONE) if result: - self._taskStatusCache[status_name] = result['id'] + self._taskStatusName2IdCache[status_name] = result['id'] return result['id'] raise KeyError('No such status: %s Valid values are: %s' % (status_name, ', '.join(self.getTaskStatusNames()))) @@ -141,16 +151,16 @@ class RADatabase: def getTaskTypeNames(self): return [x['name'] for x in self.getTaskTypes()] - def getTaskTypeId(self, type_name, from_cache=False): - if from_cache and type_name in self._taskStatusCache: - return self._taskTypeCache[type_name] + def getTaskTypeId(self, type_name, from_cache=True): + if from_cache and type_name in self._taskTypeName2IdCache: + return self._taskTypeName2IdCache[type_name] query = '''SELECT id from resource_allocation.task_type WHERE name = %s;''' result = self._executeQuery(query, [type_name], fetch=_FETCH_ONE) if result: - self._taskTypeCache[type_name] = result['id'] + self._taskTypeName2IdCache[type_name] = result['id'] return result['id'] raise KeyError('No such type: %s Valid values are: %s' % (type_name, ', '.join(self.getTaskTypeNames()))) @@ -163,22 +173,32 @@ class RADatabase: def getResourceClaimStatusNames(self): return [x['name'] for x in self.getResourceClaimStatuses()] - def getResourceClaimStatusId(self, status_name): + def getResourceClaimStatusId(self, status_name, from_cache=True): + if from_cache and status_name in self._claimStatusName2IdCache: + return self._claimStatusName2IdCache[status_name] + query = '''SELECT id from resource_allocation.resource_claim_status WHERE name = %s;''' result = self._executeQuery(query, [status_name], fetch=_FETCH_ONE) if result: + self._claimStatusName2IdCache[status_name] = result['id'] + self._claimStatusId2NameCache[result['id']] = status_name return result['id'] raise KeyError('No such status: %s. Valid values are: %s' % (status_name, ', '.join(self.getResourceClaimStatusNames()))) - def getResourceClaimStatusName(self, status_id): + def getResourceClaimStatusName(self, status_id, from_cache=True): + if from_cache and status_id in self._claimStatusId2NameCache: + return self._claimStatusId2NameCache[status_id] + query = '''SELECT name from resource_allocation.resource_claim_status WHERE id = %s;''' result = self._executeQuery(query, [status_id], fetch=_FETCH_ONE) if result: + self._claimStatusId2NameCache[status_id] = result['name'] + self._claimStatusName2IdCache[result['name']] = status_id return result['name'] raise KeyError('No such status_id: %s. Valid values are: %s' % (status_id, ', '.join([x['id'] for x in self.getResourceClaimStatuses()]))) @@ -470,6 +490,36 @@ class RADatabase: return self.cursor.rowcount > 0 + def updateTaskStartEndTimes(self, task_id, starttime=None, endtime=None, commit=True): + fields = [] + values = [] + + if starttime: + fields.append('starttime') + values.append(starttime) + + if endtime: + fields.append('endtime') + values.append(endtime) + + if not fields: + return False + + values.append(task_id) + + query = '''UPDATE resource_allocation.specification + SET ({fields}) = ({value_placeholders}) + WHERE resource_allocation.specification.id in + (SELECT t.specification_id FROM resource_allocation.task + t WHERE t.id={id_placeholder});'''.format(fields=', '.join(fields), + value_placeholders=', '.join('%s' for x in fields), + id_placeholder='%s') + self._executeQuery(query, values) + if commit: + self.commit() + + return self.cursor.rowcount > 0 + def getTaskPredecessorIds(self, id=None): query = '''SELECT * from resource_allocation.task_predecessor tp''' @@ -564,7 +614,7 @@ class RADatabase: query = '''SELECT * from resource_allocation.specification spec WHERE spec.id = (%s);''' - return list(self._executeQuery(query, [specification_id], fetch=_FETCH_ALL)) + return self._executeQuery(query, [specification_id], fetch=_FETCH_ONE) def insertSpecification(self, starttime, endtime, content, cluster, commit=True): logger.info('insertSpecification starttime=%s, endtime=%s cluster=%s' % (starttime, endtime, cluster)) @@ -680,7 +730,7 @@ class RADatabase: raise KeyError('No such unit: %s Valid values are: %s' % (unit_name, ', '.join(self.getUnitNames()))) def getResources(self, resource_ids=None, resource_types=None, include_availability=False, claimable_capacity_lower_bound=None, claimable_capacity_upper_bound=None): - '''get list of resources for the requested resource_ids and/or resource_types (may be None). + '''get list of resources for the requested resource_ids and/or resource_types (may be None). By default, for each resource, no availability and total-, used- and available capacity are returned. Specify include_availability=True to get those as well. By default, for each resource, no claimable_capacity is returned. Specify claimable_capacity_lower_bound and claimable_capacity_upper_bound to get the claimable_capacity as well. :param resource_ids: only get the resources for the given ids @@ -751,6 +801,24 @@ class RADatabase: return resources + def get_current_resource_usage(self, resource_id, claim_status='claimed'): + if isinstance(claim_status, basestring): + claim_status_id = self.getResourceClaimStatusId(claim_status) + else: + claim_status_id = claim_status + + query = '''SELECT * from resource_allocation.get_current_resource_usage(%s, %s)''' + return self._executeQuery(query, (resource_id, claim_status_id), fetch=_FETCH_ONE) + + def get_resource_usage_at_or_before(self, resource_id, timestamp, claim_status='claimed', exactly_at=False, only_before=False): + if isinstance(claim_status, basestring): + claim_status_id = self.getResourceClaimStatusId(claim_status) + else: + claim_status_id = claim_status + + query = '''SELECT * from resource_allocation.get_resource_usage_at_or_before(%s, %s, %s, %s, %s, %s)''' + return self._executeQuery(query, (resource_id, claim_status_id, timestamp, exactly_at, only_before, False), fetch=_FETCH_ONE) + def updateResourceAvailability(self, resource_id, active=None, available_capacity=None, total_capacity=None, commit=True): if active is not None: query = '''UPDATE resource_monitoring.resource_availability @@ -1024,7 +1092,8 @@ class RADatabase: return sap_ids - def getResourceClaims(self, claim_ids=None, lower_bound=None, upper_bound=None, resource_ids=None, task_ids=None, status=None, resource_type=None, extended=False, include_properties=False): + def getResourceClaims(self, claim_ids=None, lower_bound=None, upper_bound=None, resource_ids=None, task_ids=None, + status=None, resource_type=None, extended=False, include_properties=False): extended |= resource_type is not None query = '''SELECT * from %s''' % ('resource_allocation.resource_claim_extended_view' if extended else 'resource_allocation.resource_claim_view') @@ -1109,7 +1178,7 @@ class RADatabase: logger.info("found %s claims" % len(claims)) if include_properties and claims: - claimDict = {c['id']:c for c in claims} + claimDict = {c['id']: c for c in claims} claim_ids = claimDict.keys() properties = self.getResourceClaimProperties(claim_ids=claim_ids) for p in properties: @@ -1149,7 +1218,7 @@ class RADatabase: ''' insert one resource claim for the given task :param resource_id: id of the resource which is claimed - :param task_id: id of the task for which this claim is made + :param task_id: id of the task for which this claim is made :param starttime: when should this claim start? (can be different than the task's starttime) :param endtime: when should this claim end? (can be different than the task's endtime) :param claim_size: how much do you want to claim? @@ -1179,18 +1248,18 @@ class RADatabase: def insertResourceClaims(self, task_id, claims, username, user_id, commit=True): '''bulk insert of a list of resource claims for a task(_id). All claims are inserted with status tentative. :param task_id: the task(_id) for which these claims are inserted. Each claim always belongs to one task, and one task only. - :param claims: list of claims. each claim is defined by the following dict: {'resource_id': <int>, - 'starttime': <datetime>, - 'endtime': <datetime>, - 'claim_size': <int>, + :param claims: list of claims. each claim is defined by the following dict: {'resource_id': <int>, + 'starttime': <datetime>, + 'endtime': <datetime>, + 'claim_size': <int>, 'used_rcus': <??>, - 'properties': <list of tuples> #see insertResourceClaimProperties + 'properties': <list of tuples> #see insertResourceClaimProperties } :param username: the name of the user who inserts these claims (to link the this insert action to a user) (Not used yet, fill in any name) :param userid: the id of the user who inserts these claims (to link the this insert action to a user) (Not used yet, fill in any int) - :return: list of ints with the new claim id's - - claims is a list of dicts. Each dict is a claim for one resource containing the fields: + :return: list of ints with the new claim id's + + claims is a list of dicts. Each dict is a claim for one resource containing the fields: starttime, endtime, status, claim_size ''' logger.info('insertResourceClaims for task_id=%d with %d claim(s)' % (task_id, len(claims))) @@ -1231,7 +1300,7 @@ class RADatabase: claimIds = [x['id'] for x in self._executeQuery(query, fetch=_FETCH_ALL)] if not claimIds or [x for x in claimIds if x < 0]: - logger.error("One or more claims cloud not be inserted. Rolling back.") + logger.error("One or more claims could not be inserted. Rolling back.") self.rollback() return [] @@ -1258,21 +1327,24 @@ class RADatabase: self.deleteResourceClaims(resource_claim_ids=[resource_claim_id], commit=commit) def deleteResourceClaims(self, resource_claim_ids, commit=True): - query = '''DELETE FROM resource_allocation.resource_claim - WHERE resource_allocation.resource_claim.id in %s;''' - - self._executeQuery(query, [tuple(resource_claim_ids)]) - if commit: - self.commit() - return self.cursor.rowcount > 0 + if resource_claim_ids: + query = '''DELETE FROM resource_allocation.resource_claim + WHERE resource_allocation.resource_claim.id in %s;''' + + self._executeQuery(query, [tuple(resource_claim_ids)]) + if commit: + self.commit() + return self.cursor.rowcount > 0 + return True def updateResourceClaim(self, resource_claim_id, resource_id=None, task_id=None, starttime=None, endtime=None, status=None, claim_size=None, username=None, used_rcus=None, user_id=None, commit=True): - return self.updateResourceClaims([resource_claim_id], None, resource_id, task_id, starttime, endtime, status, + return self.updateResourceClaims([resource_claim_id], None, None, resource_id, task_id, starttime, endtime, status, claim_size, username, used_rcus, user_id, commit) - def updateResourceClaims(self, where_resource_claim_ids=None, where_task_ids=None, resource_id=None, task_id=None, starttime=None, endtime=None, + def updateResourceClaims(self, where_resource_claim_ids=None, where_task_ids=None, where_resource_types=None, + resource_id=None, task_id=None, starttime=None, endtime=None, status=None, claim_size=None, username=None, used_rcus=None, user_id=None, commit=True): '''Update the given paramenters on all resource claims given/delimited by where_resource_claim_ids and/or where_task_ids. @@ -1285,8 +1357,6 @@ class RADatabase: When one or more claims of a task are in conflict status, then its task is set to conflict as well, and hence cannot be scheduled. When all claims of a task are not in conflict status anymore, then the task is set to approved, and hence it is possible the schedule the task. ''' - logger.info("updateResourceClaims") - status_id = status if status is not None and isinstance(status, basestring): #convert status string to status.id @@ -1356,8 +1426,27 @@ class RADatabase: conditions.append('task_id in %s') values.append(tuple(where_task_ids)) + if where_resource_types is not None: + if isinstance(where_resource_types, basestring) or isinstance(where_resource_types, int): + where_resource_types = [where_resource_types] + elif not isinstance(where_resource_types, collections.Iterable): + where_resource_types = [where_resource_types] + + # convert any resource_type name to id + resource_type_names = set([x for x in where_resource_types if isinstance(x, basestring)]) + if resource_type_names: + resource_type_name_to_id = {x['name']:x['id'] for x in self.getResourceTypes()} + where_resource_type_ids = [resource_type_name_to_id[x] if isinstance(x, basestring) else x + for x in where_resource_types] + else: + where_resource_type_ids = [x for x in where_resource_types] + + conditions.append('resource_id in (SELECT r.id FROM virtual_instrument.resource r WHERE r.type_id in %s)') + values.append(tuple(where_resource_type_ids)) + query += ' WHERE ' + ' AND '.join(conditions) + self._executeQuery(query, values) if commit: @@ -1366,7 +1455,7 @@ class RADatabase: return self.cursor.rowcount > 0 - def updateTaskAndResourceClaims(self, task_id, starttime=None, endtime=None, task_status=None, claim_status=None, username=None, used_rcus=None, user_id=None, commit=True): + def updateTaskAndResourceClaims(self, task_id, starttime=None, endtime=None, task_status=None, claim_status=None, username=None, used_rcus=None, user_id=None, where_resource_types=None, commit=True): '''combination of updateResourceClaims and updateTask in one transaction''' updated = True @@ -1374,6 +1463,7 @@ class RADatabase: username is not None or used_rcus is not None or user_id is not None): # update the claims as well updated &= self.updateResourceClaims(where_task_ids=task_id, + where_resource_types=where_resource_types, starttime=starttime, endtime=endtime, status=claim_status, @@ -1382,28 +1472,30 @@ class RADatabase: user_id=user_id, commit=False) + if starttime or endtime: + updated &= self.updateTaskStartEndTimes(task_id, starttime=starttime, endtime=endtime, commit=False) + if task_status is not None : updated &= self.updateTask(task_id, task_status=task_status, commit=False) - if starttime or endtime: - task = self.getTask(task_id) - if task is None: - return False - updated &= self.updateSpecification(task['specification_id'], starttime=starttime, endtime=endtime, commit=False) - if commit: self.commit() return updated - def get_conflicting_overlapping_claims(self, claim_id): + def get_overlapping_claims(self, claim_id, claim_status='claimed'): '''returns a list of claimed claims which overlap with given claim and which prevent the given claim to be claimed (cause it to be in conflict)''' - query = '''SELECT * from resource_allocation.get_conflicting_overlapping_claims(%s)''' - return list(self._executeQuery(query, (claim_id,), fetch=_FETCH_ALL)) + if isinstance(claim_status, basestring): + claim_status_id = self.getResourceClaimStatusId(claim_status) + else: + claim_status_id = claim_status - def get_conflicting_overlapping_tasks(self, claim_id): + query = '''SELECT * from resource_allocation.get_overlapping_claims(%s, %s)''' + return list(self._executeQuery(query, (claim_id, claim_status_id), fetch=_FETCH_ALL)) + + def get_overlapping_tasks(self, claim_id, claim_status='claimed'): '''returns a list of tasks which overlap with given claim(s) and which prevent the given claim(s) to be claimed (cause it to be in conflict)''' - conflicting_claims = self.get_conflicting_overlapping_claims(claim_id) + conflicting_claims = self.get_overlapping_claims(claim_id, claim_status) task_ids = set([c['task_id'] for c in conflicting_claims]) return self.getTasks(task_ids=task_ids) @@ -1413,26 +1505,41 @@ class RADatabase: else: claim_status_id = claim_status + result = {'usage': 0, 'status_id': claim_status_id, 'as_of_timestamp': lower_bound, 'resource_id': resource_id} + query = '''SELECT * from resource_allocation.get_max_resource_usage_between(%s, %s, %s, %s)''' - result = self._executeQuery(query, (resource_id, claim_status_id, lower_bound, upper_bound), fetch=_FETCH_ONE) + qresult = self._executeQuery(query, (resource_id, claim_status_id, lower_bound, upper_bound), fetch=_FETCH_ONE) - if result and result.get('usage') is not None: - return result - return {'usage': 0, 'status_id': claim_status_id, 'as_of_timestamp': lower_bound, 'resource_id': resource_id} + if qresult and qresult.get('usage') is not None: + result['usage'] = qresult.get('usage') + return result def get_resource_claimable_capacity(self, resource_id, lower_bound, upper_bound): '''get the claimable capacity for the given resource within the timewindow given by lower_bound and upper_bound. this is the resource's available capacity (total-used) minus the maximum allocated usage in that timewindow.''' + if resource_id is None or lower_bound is None or upper_bound is None: + raise ValueError('resource_id and/or lower_bound and/or upper_bound cannot be None') + query = '''SELECT * from resource_allocation.get_resource_claimable_capacity_between(%s, %s, %s)''' - result = self._executeQuery(query, (resource_id, lower_bound, upper_bound), fetch=_FETCH_ONE) - if result: - return result.get('get_resource_claimable_capacity_between', 0) - return 0 + qresult = self._executeQuery(query, (resource_id, lower_bound, upper_bound), fetch=_FETCH_ONE) + if qresult: + return qresult.get('get_resource_claimable_capacity_between', 0) + else: + return 0 - def rebuild_resource_usages_table_from_claims(self): + def rebuild_resource_usages_from_claims(self, resource_id=None, claim_status=None): '''(re)builds the resource_usages table from all currently known resource_claims''' - query = '''SELECT * from resource_allocation.rebuild_resource_usages_table_from_claims()''' - self._executeQuery(query, fetch=_FETCH_NONE) + if isinstance(claim_status, basestring): + claim_status_id = self.getResourceClaimStatusId(claim_status) + else: + claim_status_id = claim_status + + if resource_id is None and claim_status_id is None: + self._executeQuery('SELECT * from resource_allocation.rebuild_resource_usages_from_claims()', fetch=_FETCH_NONE) + elif claim_status_id is None: + self._executeQuery('SELECT * from resource_allocation.rebuild_resource_usages_from_claims_for_resource(%s)', (resource_id,), fetch=_FETCH_NONE) + else: + self._executeQuery('SELECT * from resource_allocation.rebuild_resource_usages_from_claims_for_resource_of_status(%s, %s)', (resource_id, claim_status_id), fetch=_FETCH_NONE) def insertSpecificationAndTask(self, mom_id, otdb_id, task_status, task_type, starttime, endtime, content, cluster, commit=True): ''' @@ -1573,14 +1680,14 @@ class RADatabase: self.commit() return ids - def getResourceUsages(self, lower_bound=None, upper_bound=None, resource_ids=None, resource_types=None, claim_statuses=None): - """ Get the resource usages over time within the optionally given time window, resource_ids, resource_types, and/or claim_statuses. - :param lower_bound: filter for usages newer than lower_bound + def getResourceUsages(self, lower_bound=None, upper_bound=None, resource_ids=None, claim_statuses=None): + """ Get the resource usages over time within the optionally given time window, resource_ids, +and/or claim_statuses. + :param lower_bound: filter for usages newer than lower_bound :param upper_bound: filter for usages older than upper_bound - :param resource_ids: filter for usages for the given resource id(s) - :param resource_types: filter for usages for the given resource type(s) + :param resource_ids: filter for usages for the given resource id(s) :param claim_statuses: filter for usages for the given claim status(es) - :return: a nested dict with resource_id at the first level, then claim_status(name) at the second level, and then a list of time ordered usages. + :return: a nested dict with resource_id at the first level, then claim_status(name) at the second level, and then a list of time ordered usages. """ usages_per_resource = {} query = '''SELECT * from resource_allocation.resource_usage''' @@ -1604,22 +1711,6 @@ class RADatabase: conditions.append('resource_id in %s') qargs.append(tuple(resource_ids)) - if resource_types is not None: - if isinstance(resource_types, basestring): - resource_types = [resource_types] - elif not isinstance(resource_types, collections.Iterable): - resource_types = [resource_types] - - # convert any resource_type name to id - resource_type_names = set([x for x in resource_types if isinstance(x, basestring)]) - if resource_type_names: - resource_type_name_to_id = {x['name']:x['id'] for x in self.getResourceTypes()} - resource_type_ids = [resource_type_name_to_id[x] if isinstance(x, basestring) else x - for x in resource_types] - - conditions.append('type_id in %s') - qargs.append(tuple(resource_type_ids)) - if claim_statuses is not None: if isinstance(claim_statuses, basestring): claim_statuses = [claim_statuses] diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/CMakeLists.txt b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/CMakeLists.txt index def97c594aeb5b88dc0458387ca87b05ac01a067..d0443398bad2a8ca891f2d43956776ce0dcfcf8e 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/CMakeLists.txt +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/CMakeLists.txt @@ -5,7 +5,8 @@ set(sql_files add_notifications.sql add_resource_allocation_statics.sql add_virtual_instrument.sql create_database.sql - create_and_populate_database.sql) + create_and_populate_database.sql + README) install_files(/share/radb/sql FILES ${sql_files}) diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_functions_and_triggers.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_functions_and_triggers.sql index eabae3893ccaafc09d95e67f17935f7eb27daa30..9fa8caae0f520853b0a5dfb243f92e8ebcb5dffd 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_functions_and_triggers.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_functions_and_triggers.sql @@ -166,8 +166,8 @@ DECLARE min_starttime timestamp; min_inter_task_delay int; BEGIN - IF NEW.starttime >= NEW.endtime THEN - RAISE EXCEPTION 'task specification starttime >= endtime: %', NEW; + IF NEW.starttime > NEW.endtime THEN + RAISE EXCEPTION 'task specification starttime > endtime: %', NEW; END IF; --store task duration @@ -236,8 +236,8 @@ CREATE OR REPLACE FUNCTION resource_allocation.on_claim_insertupdate_check_start RETURNS trigger AS $BODY$ BEGIN - IF NEW.starttime >= NEW.endtime THEN - RAISE EXCEPTION 'claim starttime >= endtime: %', NEW; + IF NEW.starttime > NEW.endtime THEN + RAISE EXCEPTION 'claim starttime > endtime: %', NEW; END IF; RETURN NEW; END; @@ -264,23 +264,9 @@ DECLARE usage_at_or_before_end RECORD; intermediate_usage RECORD; BEGIN - -- find last known resource_usage at or before claim starttime - SELECT * FROM resource_allocation.resource_usage ru - WHERE ru.resource_id = new_claim.resource_id - AND ru.status_id = new_claim.status_id - AND ru.as_of_timestamp <= new_claim.starttime - ORDER BY ru.as_of_timestamp DESC - LIMIT 1 - INTO usage_at_or_before_start; - - -- find last known resource_usage before claim endtime - SELECT * FROM resource_allocation.resource_usage ru - WHERE ru.resource_id = new_claim.resource_id - AND ru.status_id = new_claim.status_id - AND ru.as_of_timestamp <= new_claim.endtime - ORDER BY ru.as_of_timestamp DESC - LIMIT 1 - INTO usage_at_or_before_end; + -- find resource_usage at claim starttime + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(new_claim.resource_id, new_claim.status_id, new_claim.starttime, false, false, false) into usage_at_or_before_start; + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(new_claim.resource_id, new_claim.status_id, new_claim.endtime, false, false, false) into usage_at_or_before_end; --add new_claim.claim_size at claim starttime to resource_usage depending on state of usage_at_or_before_start IF usage_at_or_before_start IS NOT NULL THEN @@ -330,27 +316,244 @@ COMMENT ON FUNCTION resource_allocation.process_new_claim_into_resource_usages(n --------------------------------------------------------------------------------------------------------------------- -CREATE OR REPLACE FUNCTION resource_allocation.rebuild_resource_usages_table_from_claims() +CREATE OR REPLACE FUNCTION resource_allocation.rebuild_resource_usages_from_claims() + RETURNS void AS +$$ +DECLARE + resource virtual_instrument.resource; +BEGIN + FOR resource IN (SELECT * FROM virtual_instrument.resource ORDER BY id) LOOP + PERFORM resource_allocation.rebuild_resource_usages_from_claims_for_resource(resource.id); + END LOOP; +END; +$$ LANGUAGE plpgsql; +ALTER FUNCTION resource_allocation.rebuild_resource_usages_from_claims() OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.rebuild_resource_usages_from_claims() + IS 'function which truncates the resource_usages table, and repopulates it by calling process_new_claim_into_resource_usages for each known claim.'; + +--------------------------------------------------------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION resource_allocation.rebuild_resource_usages_from_claims_for_resource(_resource_id int) + RETURNS void AS +$$ +DECLARE + status resource_allocation.resource_claim_status; +BEGIN + FOR status IN (SELECT * FROM resource_allocation.resource_claim_status ORDER BY id) LOOP + PERFORM resource_allocation.rebuild_resource_usages_from_claims_for_resource_of_status(_resource_id, status.id); + END LOOP; +END; +$$ LANGUAGE plpgsql; +ALTER FUNCTION resource_allocation.rebuild_resource_usages_from_claims_for_resource(_resource_id int) OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.rebuild_resource_usages_from_claims_for_resource(_resource_id int) + IS 'function which rebuilds the resource_usages table for a specific resource.'; + +--------------------------------------------------------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION resource_allocation.rebuild_resource_usages_from_claims_for_resource_of_status(_resource_id int, _status_id int) RETURNS void AS $$ DECLARE claim resource_allocation.resource_claim; + finished_claim resource_allocation.resource_claim; + tmp_usage resource_allocation.resource_usage; + new_usage_value bigint := 0; + new_usage_id integer; BEGIN - LOCK TABLE resource_allocation.resource_claim IN EXCLUSIVE MODE; + -- make sure nobody thouches the these tables while running this function + LOCK TABLE resource_allocation.resource_claim IN ACCESS SHARE MODE; LOCK TABLE resource_allocation.resource_usage IN EXCLUSIVE MODE; - TRUNCATE TABLE resource_allocation.resource_usage RESTART IDENTITY CASCADE; + LOCK TABLE resource_allocation._rebuild_usages_active_claims IN EXCLUSIVE MODE; + + -- delete the relevant usages (so we can re-enter them in this method) + DELETE FROM resource_allocation.resource_usage WHERE resource_id = _resource_id AND status_id = _status_id; + + -- make sure the helper tables are empty + TRUNCATE resource_allocation._rebuild_usages_active_claims; --tracks the 'open'/'active' claims (starttime < loop_timestamp < endtime) + TRUNCATE resource_allocation._rebuild_usages_active_usages; --will be filled with small subset of usages-table for faster lookups than in the big reource_usage table. + + -- process each claim for this _resource_id with _status_id + FOR claim IN (SELECT * FROM resource_allocation.resource_claim + WHERE resource_id = _resource_id + AND status_id = _status_id + ORDER BY starttime, endtime) + LOOP + -- keep helper table _rebuild_usages_active_usages small and quick-to-search in each iteration. + -- delete all 'closed'/'obsolete' usages from + -- any usage before the first usage before min(endtime) of the active_claims is obsolete. (yes, that's twice before) + SELECT * FROM resource_allocation._rebuild_usages_active_usages ru + WHERE ru.as_of_timestamp < (SELECT MIN(endtime) FROM resource_allocation._rebuild_usages_active_claims) + ORDER BY ru.as_of_timestamp DESC + LIMIT 1 + INTO tmp_usage; + IF tmp_usage IS NOT NULL THEN + -- remember from above? any usage before the first usage before min(starttime) of the active_claims is obsolete. + -- so, select the first usage before the usage we just found. + SELECT * FROM resource_allocation._rebuild_usages_active_usages ru + WHERE ru.as_of_timestamp < tmp_usage.as_of_timestamp + ORDER BY ru.as_of_timestamp DESC + LIMIT 1 + INTO tmp_usage; + + IF tmp_usage IS NOT NULL THEN + DELETE FROM resource_allocation._rebuild_usages_active_usages ru WHERE ru.as_of_timestamp < tmp_usage.as_of_timestamp; + END IF; + END IF; + + --'close' all finished claims (if any) + FOR finished_claim IN (SELECT * FROM resource_allocation._rebuild_usages_active_claims ac + WHERE ac.endtime <= claim.starttime + ORDER BY endtime) + LOOP + --(quick) search in the (small) _rebuild_usages_active_usages which holds only relevant usages + --find last usage at or before finished_claim.endtime + SELECT * FROM resource_allocation._rebuild_usages_active_usages ru + WHERE ru.as_of_timestamp <= finished_claim.endtime + ORDER BY ru.as_of_timestamp DESC + LIMIT 1 + INTO tmp_usage; + + IF tmp_usage IS NULL THEN + RAISE EXCEPTION 'tmp_usage should not be NULL while finishing active claims for claim % in rebuild_resource_usages_from_claims_for_resource_of_status(%, %)', finished_claim, _resource_id, _status_id; + END IF; + + -- integrate (add current value to previous value) + new_usage_value := tmp_usage.usage - finished_claim.claim_size; + + --a finished claim is 'closed' by subtracting the claim_size from the last usage value + --this happens either at an already existing usage timestamp, or at a new usage timestamp. + IF finished_claim.endtime = tmp_usage.as_of_timestamp THEN + --claim's endtime coincides with existing usage timestamp + --update the existing usage into the table + UPDATE resource_allocation.resource_usage + SET usage = new_usage_value + WHERE id = tmp_usage.id; + + --also update the usage in the the small _rebuild_usages_active_usages table. + UPDATE resource_allocation._rebuild_usages_active_usages + SET usage = new_usage_value + WHERE id = tmp_usage.id; + ELSE + --claim's endtime does not coincide with existing usage timestamp + --insert the new usage into the table + INSERT INTO resource_allocation.resource_usage (resource_id, status_id, as_of_timestamp, usage) + VALUES (_resource_id, _status_id, finished_claim.endtime, new_usage_value) RETURNING id INTO new_usage_id; + + --also add the usage to the small _rebuild_usages_active_usages table, so it can be (quickly) searched. + INSERT INTO resource_allocation._rebuild_usages_active_usages (id, resource_id, status_id, as_of_timestamp, usage) + VALUES (new_usage_id, _resource_id, _status_id, finished_claim.endtime, new_usage_value); + END IF; - FOR claim IN SELECT * FROM resource_allocation.resource_claim LOOP - PERFORM resource_allocation.process_new_claim_into_resource_usages(claim); + --now that the claim has been 'closed', remove it from the active claims + DELETE FROM resource_allocation._rebuild_usages_active_claims WHERE id = finished_claim.id; + END LOOP; -- end loop over finished claims + + --all claims which finished at or before this claim's starttime are now closed. + --now, handle the new 'active' claim + + --(quick) search in the (small) _rebuild_usages_active_usages which holds only relevant usages + --find last usage at or before claim.starttime + SELECT * FROM resource_allocation._rebuild_usages_active_usages ru + WHERE ru.as_of_timestamp <= claim.starttime + ORDER BY ru.as_of_timestamp DESC + LIMIT 1 + INTO tmp_usage; + + --this 'active' claim 'opens' also either at an already existing usage timestamp or at a new usage timestamp. + IF tmp_usage IS NOT NULL AND claim.starttime = tmp_usage.as_of_timestamp THEN + --claim's starttime coincides with existing usage timestamp + -- integrate (add current value to previous value) + new_usage_value := tmp_usage.usage + claim.claim_size; + + --update the existing usage with the new_usage_value + UPDATE resource_allocation.resource_usage + SET usage = new_usage_value + WHERE id = tmp_usage.id; + + --also update the small _rebuild_usages_active_usages table, so it can be (quickly) searched. + UPDATE resource_allocation._rebuild_usages_active_usages + SET usage = new_usage_value + WHERE id = tmp_usage.id; + ELSE + --claim's starttime does not coincide with existing usage timestamp + IF tmp_usage IS NULL THEN + -- integrate (no previous value, so start of integral) + new_usage_value := claim.claim_size; + ELSE + -- integrate (add current value to previous value) + new_usage_value := tmp_usage.usage + claim.claim_size; + END IF; + + --and insert the new usage into the table + INSERT INTO resource_allocation.resource_usage (resource_id, status_id, as_of_timestamp, usage) + VALUES (_resource_id, _status_id, claim.starttime, new_usage_value) RETURNING id INTO new_usage_id; + + --also add the usage to the small _rebuild_usages_active_usages table, so it can be (quickly) searched. + INSERT INTO resource_allocation._rebuild_usages_active_usages (id, resource_id, status_id, as_of_timestamp, usage) + VALUES (new_usage_id, _resource_id, _status_id, claim.starttime, new_usage_value); + END IF; + + --now that the claim has been 'opened', add it to the active claims + INSERT INTO resource_allocation._rebuild_usages_active_claims (id, resource_id, task_id, starttime, endtime, status_id, claim_size) + VALUES (claim.id, claim.resource_id, claim.task_id, claim.starttime, claim.endtime, claim.status_id, claim.claim_size); END LOOP; + + --all claims were processed and at least opened + --so, conclude with 'closing' all still active claims + FOR finished_claim IN (SELECT * FROM resource_allocation._rebuild_usages_active_claims ac + ORDER BY endtime) + LOOP + -- (quick) search in the (small) _rebuild_usages_active_usages which holds only relevant usages + SELECT * FROM resource_allocation._rebuild_usages_active_usages ru + WHERE ru.as_of_timestamp <= finished_claim.endtime + ORDER BY ru.as_of_timestamp DESC + LIMIT 1 + INTO tmp_usage; + + IF tmp_usage IS NULL THEN + RAISE EXCEPTION 'tmp_usage should not be NULL while finishing processing opened claims for claim % in rebuild_resource_usages_from_claims_for_resource_of_status(%, %)', finished_claim, _resource_id, _status_id; + END IF; + + -- integrate (add current value to previous value) + new_usage_value := tmp_usage.usage - finished_claim.claim_size; + + --a finished claim is 'closed' by subtracting the claim_size from the last_usage_value + --this happens either at an already existing usage timestamp, or at a new usage timestamp. + IF finished_claim.endtime = tmp_usage.as_of_timestamp THEN + --claim's endtime coincides with existing usage timestamp + UPDATE resource_allocation.resource_usage + SET usage = new_usage_value + WHERE id = tmp_usage.id; + + --also update the small _rebuild_usages_active_usages table, so it can be (quickly) searched. + UPDATE resource_allocation._rebuild_usages_active_usages + SET usage = new_usage_value + WHERE id = tmp_usage.id; + ELSE + --claim's endtime does not coincide with existing usage timestamp + --insert the new usage into the table + INSERT INTO resource_allocation.resource_usage (resource_id, status_id, as_of_timestamp, usage) + VALUES (_resource_id, _status_id, finished_claim.endtime, new_usage_value) RETURNING id INTO new_usage_id; + + --also add the usage to the small _rebuild_usages_active_usages table, so it can be (quickly) searched. + INSERT INTO resource_allocation._rebuild_usages_active_usages (id, resource_id, status_id, as_of_timestamp, usage) + VALUES (new_usage_id, _resource_id, _status_id, finished_claim.endtime, new_usage_value); + END IF; + + --now that the claim has been 'closed', remove it from the active claims + DELETE FROM resource_allocation._rebuild_usages_active_claims WHERE id = finished_claim.id; + END LOOP; + + -- wipe the helper tables + TRUNCATE resource_allocation._rebuild_usages_active_claims; + TRUNCATE resource_allocation._rebuild_usages_active_usages; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION resource_allocation.rebuild_resource_usages_table_from_claims() OWNER TO resourceassignment; -COMMENT ON FUNCTION resource_allocation.rebuild_resource_usages_table_from_claims() - IS 'function which truncates the resource_usages table, and repopulates it by calling process_new_claim_into_resource_usages for each known claim.'; +ALTER FUNCTION resource_allocation.rebuild_resource_usages_from_claims_for_resource_of_status(int, int) OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.rebuild_resource_usages_from_claims_for_resource_of_status(int, int) + IS 'function which rebuilds the resource_usages table for the claims with a specific status for a specific resource.'; --------------------------------------------------------------------------------------------------------------------- - CREATE OR REPLACE FUNCTION resource_allocation.process_old_claim_outof_resource_usages(old_claim resource_allocation.resource_claim) RETURNS void AS $$ @@ -361,39 +564,25 @@ DECLARE intermediate_usage RECORD; BEGIN -- find resource_usage at claim starttime - SELECT * FROM resource_allocation.resource_usage ru - WHERE ru.resource_id = old_claim.resource_id - AND ru.status_id = old_claim.status_id - AND ru.as_of_timestamp = old_claim.starttime - LIMIT 1 - INTO usage_at_start; - - -- find last known resource_usage at claim endtime - SELECT * FROM resource_allocation.resource_usage ru - WHERE ru.resource_id = old_claim.resource_id - AND ru.status_id = old_claim.status_id - AND ru.as_of_timestamp = old_claim.endtime - LIMIT 1 - INTO usage_at_end; + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(old_claim.resource_id, old_claim.status_id, old_claim.starttime, true, false, true) into usage_at_start; - -- both usage_at_start and usage_at_end should exist (NOT NULL) IF usage_at_start IS NULL THEN - RAISE EXCEPTION 'resource_allocation.on_delete_claim_update_resource_usages: usage_at_start should not be NULL.'; + RAISE EXCEPTION 'process_old_claim_outof_resource_usages(%) cannot find usage_at_start', old_claim; END IF; + + + -- and find resource_usage at claim endtime + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(old_claim.resource_id, old_claim.status_id, old_claim.endtime, true, false, true) into usage_at_end; + IF usage_at_end IS NULL THEN - RAISE EXCEPTION 'resource_allocation.on_delete_claim_update_resource_usages: usage_at_end should not be NULL.'; + RAISE EXCEPTION 'process_old_claim_outof_resource_usages(%) cannot find usage_at_end', old_claim; END IF; + IF usage_at_start.usage = old_claim.claim_size THEN IF usage_at_end.usage = 0 THEN -- find resource_usage before claim starttime - SELECT * FROM resource_allocation.resource_usage ru - WHERE ru.resource_id = old_claim.resource_id - AND ru.status_id = old_claim.status_id - AND ru.as_of_timestamp < old_claim.starttime - ORDER BY ru.as_of_timestamp DESC - LIMIT 1 - INTO usage_before_start; + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(old_claim.resource_id, old_claim.status_id, old_claim.starttime, false, true, false) into usage_before_start; IF usage_before_start IS NULL OR (usage_before_start IS NOT NULL AND usage_before_start.usage = 0) THEN --usage_at_start was 'caused' by this deleted claim only, so delete it @@ -434,43 +623,117 @@ COMMENT ON FUNCTION resource_allocation.process_old_claim_outof_resource_usages( --------------------------------------------------------------------------------------------------------------------- -CREATE OR REPLACE FUNCTION resource_allocation.get_resource_usage_at(_resource_id int, _claim_status_id int, _timestamp timestamp) +CREATE OR REPLACE FUNCTION resource_allocation.get_resource_usage_at_or_before(_resource_id int, _claim_status_id int, _timestamp timestamp, exactly_at boolean default false, only_before boolean default false, rebuild_usage_when_not_found boolean default false) RETURNS resource_allocation.resource_usage AS $$ DECLARE - result record; + result resource_allocation.resource_usage; BEGIN SELECT * FROM resource_allocation.resource_usage ru WHERE ru.resource_id = _resource_id AND ru.status_id = _claim_status_id - AND ru.as_of_timestamp < _timestamp + AND ru.as_of_timestamp <= _timestamp ORDER BY ru.as_of_timestamp DESC LIMIT 1 INTO result; + -- check if as_of_timestamp is exactly_at _timestamp + IF exactly_at AND result IS NOT NULL THEN + IF result.as_of_timestamp <> _timestamp THEN + result := NULL; + END IF; + END IF; + + -- check if as_of_timestamp is before _timestamp + IF only_before AND result IS NOT NULL THEN + IF result.as_of_timestamp >= _timestamp THEN + result := NULL; + END IF; + END IF; + + -- rebuild usage when not found + IF rebuild_usage_when_not_found AND result IS NULL THEN + RAISE NOTICE 'get_resource_usage_at_or_before(_resource_id=%, status_id=%, timestamp=%, exactly_at=%, only_before=%, rebuild_usage_when_not_found=%): result should not be NULL. Rebuilding usages table for resource %.', _resource_id, _claim_status_id, _timestamp, exactly_at, only_before, rebuild_usage_when_not_found, _resource_id; + PERFORM resource_allocation.rebuild_resource_usages_from_claims_for_resource_of_status(_resource_id, _claim_status_id); + RAISE NOTICE 'get_resource_usage_at_or_before(_resource_id=%, status_id=%, timestamp=%, exactly_at=%, only_before=%, rebuild_usage_when_not_found=%): Finished rebuilding usages table for resource %.', _resource_id, _claim_status_id, _timestamp, exactly_at, only_before, rebuild_usage_when_not_found, _resource_id; + + -- try again, but now without the option to rebuild_usage_when_not_found (to prevent endless recursion) + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(_resource_id, _claim_status_id, _timestamp, exactly_at, only_before, false) INTO result; + RAISE NOTICE 'get_resource_usage_at_or_before(_resource_id=%, status_id=%, timestamp=%, exactly_at=%, only_before=%, rebuild_usage_when_not_found=%): after rebuild, result=%.', _resource_id, _claim_status_id, _timestamp, exactly_at, only_before, rebuild_usage_when_not_found, result; + END IF; + + IF result IS NULL THEN + -- if result is still null (after possible rebuild etc), then return a 'default' usage of 0 + result.resource_id = _resource_id; + result.status_id = _claim_status_id; + result.as_of_timestamp = _timestamp; + result.usage = 0; + END IF; + RETURN result; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION resource_allocation.get_resource_usage_at(_resource_id int, _claim_status_id int, _timestamp timestamp) OWNER TO resourceassignment; -COMMENT ON FUNCTION resource_allocation.get_resource_usage_at(_resource_id int, _claim_status_id int, _timestamp timestamp) +ALTER FUNCTION resource_allocation.get_resource_usage_at_or_before(int, int, timestamp, boolean, boolean, boolean) OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.get_resource_usage_at_or_before(int, int, timestamp, boolean, boolean, boolean) IS 'get the resource usage for the given _resource_id for claims with given _claim_status_id at the given _timestamp'; --------------------------------------------------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION utcnow() + RETURNS timestamp AS +$$ + SELECT NOW() AT TIME ZONE 'UTC'; +$$ LANGUAGE SQL; +ALTER FUNCTION utcnow() OWNER TO resourceassignment; +COMMENT ON FUNCTION utcnow() + IS 'get the current time in utc timezone as timestamp (without timezone)'; + +--------------------------------------------------------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION resource_allocation.get_current_resource_usage(_resource_id int, _claim_status_id int) + RETURNS resource_allocation.resource_usage AS +$$ +DECLARE + result resource_allocation.resource_usage; + now timestamp; +BEGIN + SELECT * FROM utcnow() INTO now; + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(_resource_id, _claim_status_id, now, false, false, false) into result; + RETURN result; +END; +$$ LANGUAGE plpgsql; +ALTER FUNCTION resource_allocation.get_current_resource_usage(_resource_id int, _claim_status_id int) OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.get_current_resource_usage(_resource_id int, _claim_status_id int) + IS 'get the current resource usage for the given _resource_id for claims with given _claim_status_id'; + +--------------------------------------------------------------------------------------------------------------------- + CREATE OR REPLACE FUNCTION resource_allocation.get_max_resource_usage_between(_resource_id int, _claim_status_id int, _lower timestamp, _upper timestamp) RETURNS resource_allocation.resource_usage AS $$ DECLARE - result record; + max_resource_usage_in_time_window record; + max_resource_at_or_before_starttime record; BEGIN + SELECT * FROM resource_allocation.get_resource_usage_at_or_before(_resource_id, _claim_status_id, _lower, false, false, false) into max_resource_at_or_before_starttime; + SELECT * FROM resource_allocation.resource_usage ru WHERE ru.resource_id = _resource_id AND ru.status_id = _claim_status_id AND ru.as_of_timestamp >= _lower - AND ru.as_of_timestamp <= _upper + AND ru.as_of_timestamp < _upper ORDER BY ru.usage DESC - LIMIT 1 INTO result; + LIMIT 1 INTO max_resource_usage_in_time_window; - RETURN result; + IF max_resource_usage_in_time_window IS NOT NULL THEN + IF max_resource_usage_in_time_window.usage > max_resource_at_or_before_starttime.usage THEN + RETURN max_resource_usage_in_time_window; + ELSE + RETURN max_resource_at_or_before_starttime; + END IF; + ELSE + -- could also be NULL but that is checked for elsewhere + RETURN max_resource_at_or_before_starttime; + END IF; END; $$ LANGUAGE plpgsql; ALTER FUNCTION resource_allocation.get_max_resource_usage_between(_resource_id int, _claim_status_id int, _lower timestamp, _upper timestamp) OWNER TO resourceassignment; @@ -485,22 +748,43 @@ $$ DECLARE claimed_status_id int := 1; --beware: hard coded instead of lookup for performance max_resource_usage resource_allocation.resource_usage; - max_resource_usage_value int; + max_resource_usage_value bigint; available_capacity bigint; + total_capacity bigint; + current_claimed_usage bigint; BEGIN - SELECT * FROM resource_allocation.get_max_resource_usage_between(_resource_id, claimed_status_id, _lower, _upper) INTO max_resource_usage; + SELECT usage FROM resource_allocation.get_max_resource_usage_between(_resource_id, claimed_status_id, _lower, _upper) INTO max_resource_usage_value; - IF max_resource_usage IS NULL THEN + IF max_resource_usage_value IS NULL THEN max_resource_usage_value := 0; - ELSE - max_resource_usage_value := max_resource_usage.usage; END IF; - SELECT available FROM resource_monitoring.resource_capacity WHERE resource_id = _resource_id LIMIT 1 INTO available_capacity; - RETURN available_capacity - max_resource_usage_value; + -- determine the available_capacity for this resource + -- available_capacity is a truly measured metric (by tools like df (disk-free)) + SELECT available, total FROM resource_monitoring.resource_capacity WHERE resource_id = _resource_id LIMIT 1 INTO available_capacity, total_capacity; + + -- determine how much of the used_capacity is 'accounted for' by claims. + -- this is a best guess of the amount of data which we know that should be on the resource. + -- we can only 'measure' that at this moment, + -- so take the current resource usage + SELECT usage FROM resource_allocation.get_current_resource_usage(_resource_id, claimed_status_id) INTO current_claimed_usage; + + IF available_capacity = total_capacity THEN + --this is not a monitored resource, and hence we do not know how much space is actually available. + --make a best guess by subtracting the current_claimed_usage from the total_capacity + + RETURN total_capacity - max_resource_usage_value; + ELSE + --this is a monitored resource, and the claimable_capacity is not just the free space (available_capacity) at this moment! + -- we have to take into account what we know about already claimed portions, + -- both at this moment (current_claimed_usage) and for the planned claim (max_resource_usage_value, between _lower and _upper) + + RETURN available_capacity + current_claimed_usage - max_resource_usage_value; + END IF; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION resource_allocation.get_resource_claimable_capacity_between(_resource_id int, _lower timestamp, _upper timestamp) OWNER TO resourceassignment; +ALTER FUNCTION resource_allocation.get_resource_claimable_capacity_between(_resource_id int, _lower timestamp, _upper timestamp) + OWNER TO resourceassignment; COMMENT ON FUNCTION resource_allocation.get_resource_claimable_capacity_between(_resource_id int, _lower timestamp, _upper timestamp) IS 'get the maximum resource usage for the given _resource_id for claims with given _claim_status_id in the period between the given _lower and _upper timestamps'; @@ -510,33 +794,11 @@ CREATE OR REPLACE FUNCTION resource_allocation.has_conflict_with_overlapping_cla RETURNS boolean AS $$ DECLARE - claim_claimed_status_id int := 1; --beware: hard coded instead of lookup for performance free_claimable_capacity bigint; - overlapping_claims_min_starttime timestamp; - overlapping_claims_max_endtime timestamp; BEGIN - -- this function is quite similar to resource_allocation.get_conflicting_overlapping_claims - -- for performance reasons we repeat the common code here instead of wrapping the common code in a function - - --get all overlapping_claims, check whether they cause a conflict or not. - SET LOCAL client_min_messages=warning; --prevent "table overlapping_claims does not exist, skipping" message - DROP TABLE IF EXISTS overlapping_claims; -- TODO: use CREATE TEMPORARY TABLE IF NOT EXISTS when we will use postgres 9.5+ - CREATE TEMPORARY TABLE overlapping_claims - ON COMMIT DROP - AS SELECT * FROM resource_allocation.resource_claim rc - WHERE rc.resource_id = claim.resource_id - AND rc.status_id = claim_claimed_status_id - AND rc.id <> claim.id - AND rc.endtime >= claim.starttime - AND rc.starttime < claim.endtime; - - --get the full time window of the overlapping claims - SELECT min(starttime) FROM overlapping_claims INTO overlapping_claims_min_starttime; - SELECT max(endtime) FROM overlapping_claims INTO overlapping_claims_max_endtime; - - -- get the free free_claimable_capacity for this resource for the full overlapping claim time window + -- get the free free_claimable_capacity for this resource for the claim's time window -- this does not include the current claim which is (or at least should be) tentative. - SELECT * FROM resource_allocation.get_resource_claimable_capacity_between(claim.resource_id, overlapping_claims_min_starttime, overlapping_claims_max_endtime) INTO free_claimable_capacity; + SELECT * FROM resource_allocation.get_resource_claimable_capacity_between(claim.resource_id, claim.starttime, claim.endtime) INTO free_claimable_capacity; return claim.claim_size > free_claimable_capacity; END; @@ -547,7 +809,7 @@ COMMENT ON FUNCTION resource_allocation.has_conflict_with_overlapping_claims(cla --------------------------------------------------------------------------------------------------------------------- -CREATE OR REPLACE FUNCTION resource_allocation.get_conflicting_overlapping_claims(claim_id int) +CREATE OR REPLACE FUNCTION resource_allocation.get_overlapping_claims(claim_id int, status int default 1) --beware: hard coded status=1 for claimed claims RETURNS SETOF resource_allocation.resource_claim AS $$ DECLARE @@ -558,44 +820,34 @@ BEGIN LIMIT 1 INTO claim; - RETURN QUERY SELECT * FROM resource_allocation.get_conflicting_overlapping_claims(claim); + RETURN QUERY SELECT * FROM resource_allocation.get_overlapping_claims(claim, status); END; $$ LANGUAGE plpgsql; -ALTER FUNCTION resource_allocation.get_conflicting_overlapping_claims(claim_id int) OWNER TO resourceassignment; -COMMENT ON FUNCTION resource_allocation.get_conflicting_overlapping_claims(claim_id int) +ALTER FUNCTION resource_allocation.get_overlapping_claims(int, int) OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.get_overlapping_claims(int, int) IS 'get set of (claimed) claims which cause the given claim to have conflict status.'; --------------------------------------------------------------------------------------------------------------------- -CREATE OR REPLACE FUNCTION resource_allocation.get_conflicting_overlapping_claims(claim resource_allocation.resource_claim) +CREATE OR REPLACE FUNCTION resource_allocation.get_overlapping_claims(claim resource_allocation.resource_claim, status int default 1) --beware: hard coded status=1 for claimed claims RETURNS SETOF resource_allocation.resource_claim AS $$ -DECLARE - claim_claimed_status_id int := 1; --beware: hard coded instead of lookup for performance BEGIN -- this function is quite similar to resource_allocation.has_conflict_with_overlapping_claims -- for performance reasons we repeat the common code here instead of wrapping the common code in a function --get all overlapping_claims, check whether they cause a conflict or not. - SET LOCAL client_min_messages=warning; --prevent "table overlapping_claims does not exist, skipping" message - DROP TABLE IF EXISTS overlapping_claims; -- TODO: use CREATE TEMPORARY TABLE IF NOT EXISTS when we will use postgres 9.5+ - CREATE TEMPORARY TABLE overlapping_claims - ON COMMIT DROP - AS SELECT * FROM resource_allocation.resource_claim rc + RETURN QUERY SELECT * FROM resource_allocation.resource_claim rc WHERE rc.resource_id = claim.resource_id - AND rc.status_id = claim_claimed_status_id + AND rc.status_id = status AND rc.id <> claim.id AND rc.endtime >= claim.starttime AND rc.starttime < claim.endtime; - - RETURN QUERY SELECT * - FROM overlapping_claims oc - WHERE (SELECT * FROM resource_allocation.get_resource_claimable_capacity_between(claim.resource_id, oc.starttime, oc.endtime)) < claim.claim_size; END; $$ LANGUAGE plpgsql; -ALTER FUNCTION resource_allocation.get_conflicting_overlapping_claims(claim resource_allocation.resource_claim) OWNER TO resourceassignment; -COMMENT ON FUNCTION resource_allocation.get_conflicting_overlapping_claims(claim resource_allocation.resource_claim) - IS 'get set of (claimed) claims which cause the given claim to have conflict status.'; +ALTER FUNCTION resource_allocation.get_overlapping_claims(resource_allocation.resource_claim, int) OWNER TO resourceassignment; +COMMENT ON FUNCTION resource_allocation.get_overlapping_claims(resource_allocation.resource_claim, int) + IS 'get set of claims (with given status) which overlap with the given claim.'; --------------------------------------------------------------------------------------------------------------------- @@ -608,6 +860,9 @@ DECLARE claim_conflict_status_id int := 2; --beware: hard coded instead of lookup for performance task_approved_status_id int := 300; --beware: hard coded instead of lookup for performance task_conflict_status_id int := 335; --beware: hard coded instead of lookup for performance + task_prescheduled_status_id int := 350; --beware: hard coded instead of lookup for performance + task_scheduled_status_id int := 400; --beware: hard coded instead of lookup for performance + task_queued_status_id int := 500; --beware: hard coded instead of lookup for performance claim_has_conflicts boolean; BEGIN --order of following steps is important, do not reorder the steps @@ -615,40 +870,64 @@ BEGIN -- bounce any inserted claim which is not tentative IF TG_OP = 'INSERT' THEN IF NEW.status_id <> claim_tentative_status_id THEN - RAISE EXCEPTION 'newly inserted claims should not have status other than tentative; claim: %', NEW; + RAISE EXCEPTION 'newly inserted claims should not have status other than tentative (%); new claim: % has status %', claim_tentative_status_id, NEW, NEW.status_id; END IF; END IF; IF TG_OP = 'UPDATE' THEN -- bounce any updated claim which has conflict state, but is tried to be updated to claimed + -- only this function can 'reset' the conflict state back to tentative! IF NEW.status_id = claim_claimed_status_id AND OLD.status_id = claim_conflict_status_id THEN RAISE EXCEPTION 'cannot update claim-in-conflict to status claimed; old:% new:%', OLD, NEW; END IF; - -- bounce any updated claim which has claimed state onto which other properties are changed - IF NEW.status_id = claim_claimed_status_id AND OLD.status_id = NEW.status_id THEN - RAISE EXCEPTION 'updates on properties of claimed claim are not allowed; old:% new:%', OLD, NEW; + -- bounce any claim_size updates on claimed claims + IF NEW.status_id = claim_claimed_status_id AND OLD.claim_size <> NEW.claim_size THEN + RAISE EXCEPTION 'cannot update claim size on claimed claim; old:% new:%', OLD, NEW; + END IF; + + -- bounce any task_id updates + IF OLD.task_id <> NEW.task_id THEN + RAISE EXCEPTION 'cannot change the task to which a claim belongs; old:% new:%', OLD, NEW; + END IF; + + -- bounce any resource_id updates + IF OLD.resource_id <> NEW.resource_id THEN + RAISE EXCEPTION 'cannot change the resource to which a claim belongs; old:% new:%', OLD, NEW; END IF; END IF; - IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN - --only check claim if in tentative/conflict status, and status or claim_size or start/end time changed - IF NEW.status_id = claim_tentative_status_id OR NEW.status_id = claim_conflict_status_id THEN - IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND (OLD.status_id <> NEW.status_id OR - OLD.claim_size <> NEW.claim_size OR - OLD.starttime <> NEW.starttime OR - OLD.endtime <> NEW.endtime)) THEN - --check if claim fits or has conflicts - SELECT * FROM resource_allocation.has_conflict_with_overlapping_claims(NEW) INTO claim_has_conflicts; - - IF claim_has_conflicts AND NEW.status_id <> claim_conflict_status_id THEN + IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN + --update the resource usages affected by this claim + --do this before we check for conflicts, because this claim might be shifted for example + --which might influence the resource_usages which determine wheter a claim fits. + PERFORM resource_allocation.process_old_claim_outof_resource_usages(OLD); + END IF; + + --only check claim if status and/or claim_size and/or start/end time changed + IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND (OLD.status_id <> NEW.status_id OR + OLD.claim_size <> NEW.claim_size OR + OLD.starttime <> NEW.starttime OR + OLD.endtime <> NEW.endtime)) THEN + --check if claim fits or has conflicts + SELECT * FROM resource_allocation.has_conflict_with_overlapping_claims(NEW) INTO claim_has_conflicts; + + IF claim_has_conflicts THEN + IF NEW.status_id <> claim_conflict_status_id THEN + -- only set claims to conflict if task status <= queued + -- when a claim goes to conflict, then so does it's task, and we don't want that for running/finished/aborted tasks + IF EXISTS (SELECT 1 FROM resource_allocation.task + WHERE id=NEW.task_id + AND status_id = ANY(ARRAY[300, 335, 350, 400, 500])) THEN -- hardcoded tasks statuses <= queued -- conflict with others, so set claim status to conflict NEW.status_id := claim_conflict_status_id; - ELSIF NOT claim_has_conflicts AND NEW.status_id <> claim_tentative_status_id THEN - -- no conflict (anymore) with others, so set claim status to tentative - NEW.status_id := claim_tentative_status_id; END IF; END IF; + ELSE + -- no conflict (anymore) with others, so set claim status to tentative if currently in conflict + IF NEW.status_id = claim_conflict_status_id THEN + NEW.status_id := claim_tentative_status_id; + END IF; END IF; END IF; @@ -687,10 +966,8 @@ DECLARE affected_claim resource_allocation.resource_claim; claim_has_conflicts boolean; BEGIN - IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN - --update the resource usages affected by this claim - PERFORM resource_allocation.process_old_claim_outof_resource_usages(OLD); - END IF; + --do not process_old_claim_outof_resource_usages(OLD) + --because that has been done already in before_claim_insertupdatedelete IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN --update the resource usages affected by this claim diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_resource_allocation_statics.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_resource_allocation_statics.sql index 9031269c24df3efc0884de2dd445e8e455ae63af..e56bd04d19edfee931c0222d488011d4c306fd92 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_resource_allocation_statics.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_resource_allocation_statics.sql @@ -7,7 +7,7 @@ INSERT INTO resource_allocation.task_status VALUES (200, 'prepared'), (300, 'app (1150, 'error'), (1200, 'obsolete'); -- This is the list from OTDB, we'll need to merge it with the list from MoM in the future, might use different indexes? INSERT INTO resource_allocation.task_type VALUES (0, 'observation'),(1, 'pipeline'),(2, 'reservation'); -- We'll need more types INSERT INTO resource_allocation.resource_claim_status VALUES (0, 'tentative'), (1, 'claimed'), (2, 'conflict'); -INSERT INTO resource_allocation.resource_claim_property_type VALUES (0, 'nr_of_is_files'),(1, 'nr_of_cs_files'),(2, 'nr_of_uv_files'),(3, 'nr_of_im_files'),(4, 'nr_of_img_files'),(5, 'nr_of_pulp_files'),(6, 'nr_of_cs_stokes'),(7, 'nr_of_is_stokes'),(8, 'is_file_size'),(9, 'cs_file_size'),(10, 'uv_file_size'),(11, 'im_file_size'),(12, 'img_file_size'),(13, 'nr_of_pulp_files'),(14, 'nr_of_cs_parts'),(15, 'start_sb_nr'),(16,'uv_otdb_id'),(17,'cs_otdb_id'),(18,'is_otdb_id'),(19,'im_otdb_id'),(20,'img_otdb_id'),(21,'pulp_otdb_id'),(22, 'is_tab_nr'),(23, 'start_sbg_nr'),(24, 'pulp_file_size'); +INSERT INTO resource_allocation.resource_claim_property_type VALUES (0, 'nr_of_is_files'),(1, 'nr_of_cs_files'),(2, 'nr_of_uv_files'),(3, 'nr_of_im_files'),(4, 'nr_of_img_files'),/*(5, 'nr_of_pulp_files'),unused duplicate #10869*/(6, 'nr_of_cs_stokes'),(7, 'nr_of_is_stokes'),(8, 'is_file_size'),(9, 'cs_file_size'),(10, 'uv_file_size'),(11, 'im_file_size'),(12, 'img_file_size'),(13, 'nr_of_pulp_files'),(14, 'nr_of_cs_parts'),(15, 'start_sb_nr'),(16,'uv_otdb_id'),(17,'cs_otdb_id'),(18,'is_otdb_id'),(19,'im_otdb_id'),(20,'img_otdb_id'),(21,'pulp_otdb_id'),(22, 'is_tab_nr'),(23, 'start_sbg_nr'),(24, 'pulp_file_size'); INSERT INTO resource_allocation.resource_claim_property_io_type VALUES (0, 'output'),(1, 'input'); INSERT INTO resource_allocation.config VALUES (0, 'max_fill_ratio_CEP4_storage', '0.85'), (1, 'claim_timeout', '172800'), (2, 'min_inter_task_delay', '60'), (3, 'max_fill_ratio_CEP4_bandwidth', '0.75'); -- Just some values 172800 is two days in seconds INSERT INTO resource_allocation.conflict_reason diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_virtual_instrument.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_virtual_instrument.sql index c1b0bfb096e42445b827677321c92f2b04f01e4b..9a9b2ea43ec8a31bc008996f143b482acce39d88 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_virtual_instrument.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/add_virtual_instrument.sql @@ -21,7 +21,7 @@ INSERT INTO virtual_instrument.resource_group_type VALUES (0, 'instrument'), (1, INSERT INTO virtual_instrument.resource_group VALUES (0, 'LOFAR', 0), (1, 'CEP4', 1), (2, 'COBALT', 1), (3, 'computenodes', 4), (4, 'gpunodes', 4), (5, 'cpunode01', 5), (6, 'cpunode02', 5), (7, 'cpunode03', 5), (8, 'cpunode04', 5), (9, 'cpunode05', 5), (10, 'cpunode06', 5), (11, 'cpunode07', 5), (12, 'cpunode08', 5), (13, 'cpunode09', 5), (14, 'cpunode10', 5), (15, 'cpunode11', 5), (16, 'cpunode12', 5), (17, 'cpunode13', 5), (18, 'cpunode14', 5), (19, 'cpunode15', 5), (20, 'cpunode16', 5), (21, 'cpunode17', 5), (22, 'cpunode18', 5), (23, 'cpunode19', 5), (24, 'cpunode20', 5), (25, 'cpunode21', 5), (26, 'cpunode22', 5), (27, 'cpunode23', 5), (28, 'cpunode24', 5), (29, 'cpunode25', 5), (30, 'cpunode26', 5), (31, 'cpunode27', 5), (32, 'cpunode28', 5), (33, 'cpunode29', 5), (34, 'cpunode30', 5), (35, 'cpunode31', 5), (36, 'cpunode32', 5), (37, 'cpunode33', 5), (38, 'cpunode34', 5), (39, 'cpunode35', 5), (40, 'cpunode36', 5), (41, 'cpunode37', 5), (42, 'cpunode38', 5), (43, 'cpunode39', 5), (44, 'cpunode40', 5), (45, 'cpunode41', 5), (46, 'cpunode42', 5), (47, 'cpunode43', 5), (48, 'cpunode44', 5), (49, 'cpunode45', 5), (50, 'cpunode46', 5), (51, 'cpunode47', 5), (52, 'cpunode48', 5), (53, 'cpunode49', 5), (54, 'cpunode50', 5), (55, 'cbt001', 5), (56, 'cbt002', 5), (57, 'cbt003', 5), (58, 'cbt004', 5), (59, 'cbt005', 5), (60, 'cbt006', 5), (61, 'cbt007', 5), (62, 'cbt008', 5), (63, 'DRAGNET', 1), (64, 'drgnodes', 4), (65, 'dragproc', 5), (66, 'drg01', 5), (67, 'drg01-data1', 4), (68, 'drg01-data2', 4), (69, 'drg02', 5), (70, 'drg02-data1', 4), (71, 'drg02-data2', 4), (72, 'drg03', 5), (73, 'drg03-data1', 4), (74, 'drg03-data2', 4), (75, 'drg04', 5), (76, 'drg04-data1', 4), (77, 'drg04-data2', 4), (78, 'drg05', 5), (79, 'drg05-data1', 4), (80, 'drg05-data2', 4), (81, 'drg06', 5), (82, 'drg06-data1', 4), (83, 'drg06-data2', 4), (84, 'drg07', 5), (85, 'drg07-data1', 4), (86, 'drg07-data2', 4), (87, 'drg08', 5), (88, 'drg08-data1', 4), (89, 'drg08-data2', 4), (90, 'drg09', 5), (91, 'drg09-data1', 4), (92, 'drg09-data2', 4), (93, 'drg10', 5), (94, 'drg10-data1', 4), (95, 'drg10-data2', 4), (96, 'drg11', 5), (97, 'drg11-data1', 4), (98, 'drg11-data2', 4), (99, 'drg12', 5), (100, 'drg12-data1', 4), (101, 'drg12-data2', 4), (102, 'drg13', 5), (103, 'drg13-data1', 4), (104, 'drg13-data2', 4), (105, 'drg14', 5), (106, 'drg14-data1', 4), (107, 'drg14-data2', 4), (108, 'drg15', 5), (109, 'drg15-data1', 4), (110, 'drg15-data2', 4), (111, 'drg16', 5), (112, 'drg16-data1', 4), (113, 'drg16-data2', 4), (114, 'drg17', 5), (115, 'drg17-data1', 4), (116, 'drg17-data2', 4), (117, 'drg18', 5), (118, 'drg18-data1', 4), (119, 'drg18-data2', 4), (120, 'drg19', 5), (121, 'drg19-data1', 4), (122, 'drg19-data2', 4), (123, 'drg20', 5), (124, 'drg20-data1', 4), (125, 'drg20-data2', 4), (126, 'drg21', 5), (127, 'drg21-data1', 4), (128, 'drg21-data2', 4), (129, 'drg22', 5), (130, 'drg22-data1', 4), (131, 'drg22-data2', 4), (132, 'drg23', 5), (133, 'drg23-data1', 4), (134, 'drg23-data2', 4), (135, 'CORE', 2), (136, 'REMOTE', 2), (137, 'INTERNATIONAL', 2), (138, 'SUPERTERP', 4), (139, 'ALL', 4), (140, 'NL', 4), (141, 'CoreSansST', 4), (142, 'VLBI', 4), (143, 'AARTFAAC', 4), (144, 'CORE2KM', 4), (145, 'LORA', 4), (149, 'CS001', 3), (150, 'CS002', 3), (151, 'CS003', 3), (152, 'CS004', 3), (153, 'CS005', 3), (154, 'CS006', 3), (155, 'CS007', 3), (156, 'CS011', 3), (157, 'CS013', 3), (158, 'CS017', 3), (159, 'CS021', 3), (160, 'CS024', 3), (161, 'CS026', 3), (162, 'CS028', 3), (163, 'CS030', 3), (164, 'CS031', 3), (165, 'CS032', 3), (166, 'CS101', 3), (167, 'CS103', 3), (168, 'CS201', 3), (169, 'CS301', 3), (170, 'CS302', 3), (171, 'CS401', 3), (172, 'CS501', 3), (173, 'RS106', 3), (174, 'RS205', 3), (175, 'RS208', 3), (176, 'RS210', 3), (177, 'RS305', 3), (178, 'RS306', 3), (179, 'RS307', 3), (180, 'RS310', 3), (181, 'RS406', 3), (182, 'RS407', 3), (183, 'RS408', 3), (184, 'RS409', 3), (185, 'RS503', 3), (186, 'RS508', 3), (187, 'RS509', 3), (188, 'DE601', 3), (189, 'DE602', 3), (190, 'DE603', 3), (191, 'DE604', 3), (192, 'DE605', 3), (193, 'FR606', 3), (194, 'SE607', 3), (195, 'UK608', 3), (196, 'DE609', 3), (197, 'PL610', 3), (198, 'PL611', 3), (199, 'PL612', 3), (200, 'IE613', 3), (201, 'IS614', 3), (202, 'TEST1', 3), (206, 'CS001RSP0', 6), (207, 'CS001RSP1', 6), (208, 'CS002RSP0', 6), (209, 'CS002RSP1', 6), (210, 'CS003RSP0', 6), (211, 'CS003RSP1', 6), (212, 'CS004RSP0', 6), (213, 'CS004RSP1', 6), (214, 'CS005RSP0', 6), (215, 'CS005RSP1', 6), (216, 'CS006RSP0', 6), (217, 'CS006RSP1', 6), (218, 'CS007RSP0', 6), (219, 'CS007RSP1', 6), (220, 'CS011RSP0', 6), (221, 'CS011RSP1', 6), (222, 'CS013RSP0', 6), (223, 'CS013RSP1', 6), (224, 'CS017RSP0', 6), (225, 'CS017RSP1', 6), (226, 'CS021RSP0', 6), (227, 'CS021RSP1', 6), (228, 'CS024RSP0', 6), (229, 'CS024RSP1', 6), (230, 'CS026RSP0', 6), (231, 'CS026RSP1', 6), (232, 'CS028RSP0', 6), (233, 'CS028RSP1', 6), (234, 'CS030RSP0', 6), (235, 'CS030RSP1', 6), (236, 'CS031RSP0', 6), (237, 'CS031RSP1', 6), (238, 'CS032RSP0', 6), (239, 'CS032RSP1', 6), (240, 'CS101RSP0', 6), (241, 'CS101RSP1', 6), (242, 'CS103RSP0', 6), (243, 'CS103RSP1', 6), (244, 'CS201RSP0', 6), (245, 'CS201RSP1', 6), (246, 'CS301RSP0', 6), (247, 'CS301RSP1', 6), (248, 'CS302RSP0', 6), (249, 'CS302RSP1', 6), (250, 'CS401RSP0', 6), (251, 'CS401RSP1', 6), (252, 'CS501RSP0', 6), (253, 'CS501RSP1', 6), (254, 'RS106RSP', 6), (255, 'RS205RSP', 6), (256, 'RS208RSP', 6), (257, 'RS210RSP', 6), (258, 'RS305RSP', 6), (259, 'RS306RSP', 6), (260, 'RS307RSP', 6), (261, 'RS310RSP', 6), (262, 'RS406RSP', 6), (263, 'RS407RSP', 6), (264, 'RS408RSP', 6), (265, 'RS409RSP', 6), (266, 'RS503RSP', 6), (267, 'RS508RSP', 6), (268, 'RS509RSP', 6), (269, 'DE601RSP', 6), (270, 'DE602RSP', 6), (271, 'DE603RSP', 6), (272, 'DE604RSP', 6), (273, 'DE605RSP', 6), (274, 'FR606RSP', 6), (275, 'SE607RSP', 6), (276, 'UK608RSP', 6), (277, 'DE609RSP', 6), (278, 'PL610RSP', 6), (279, 'PL611RSP', 6), (280, 'PL612RSP', 6), (281, 'IE613RSP', 6), (282, 'IS614RSP', 6), (283, 'TEST1RSP', 6); INSERT INTO virtual_instrument.resource VALUES (0, 'cpunode01_bandwidth', 3), (1, 'cpunode01_processors', 4), (2, 'cpunode02_bandwidth', 3), (3, 'cpunode02_processors', 4), (4, 'cpunode03_bandwidth', 3), (5, 'cpunode03_processors', 4), (6, 'cpunode04_bandwidth', 3), (7, 'cpunode04_processors', 4), (8, 'cpunode05_bandwidth', 3), (9, 'cpunode05_processors', 4), (10, 'cpunode06_bandwidth', 3), (11, 'cpunode06_processors', 4), (12, 'cpunode07_bandwidth', 3), (13, 'cpunode07_processors', 4), (14, 'cpunode08_bandwidth', 3), (15, 'cpunode08_processors', 4), (16, 'cpunode09_bandwidth', 3), (17, 'cpunode09_processors', 4), (18, 'cpunode10_bandwidth', 3), (19, 'cpunode10_processors', 4), (20, 'cpunode11_bandwidth', 3), (21, 'cpunode11_processors', 4), (22, 'cpunode12_bandwidth', 3), (23, 'cpunode12_processors', 4), (24, 'cpunode13_bandwidth', 3), (25, 'cpunode13_processors', 4), (26, 'cpunode14_bandwidth', 3), (27, 'cpunode14_processors', 4), (28, 'cpunode15_bandwidth', 3), (29, 'cpunode15_processors', 4), (30, 'cpunode16_bandwidth', 3), (31, 'cpunode16_processors', 4), (32, 'cpunode17_bandwidth', 3), (33, 'cpunode17_processors', 4), (34, 'cpunode18_bandwidth', 3), (35, 'cpunode18_processors', 4), (36, 'cpunode19_bandwidth', 3), (37, 'cpunode19_processors', 4), (38, 'cpunode20_bandwidth', 3), (39, 'cpunode20_processors', 4), (40, 'cpunode21_bandwidth', 3), (41, 'cpunode21_processors', 4), (42, 'cpunode22_bandwidth', 3), (43, 'cpunode22_processors', 4), (44, 'cpunode23_bandwidth', 3), (45, 'cpunode23_processors', 4), (46, 'cpunode24_bandwidth', 3), (47, 'cpunode24_processors', 4), (48, 'cpunode25_bandwidth', 3), (49, 'cpunode25_processors', 4), (50, 'cpunode26_bandwidth', 3), (51, 'cpunode26_processors', 4), (52, 'cpunode27_bandwidth', 3), (53, 'cpunode27_processors', 4), (54, 'cpunode28_bandwidth', 3), (55, 'cpunode28_processors', 4), (56, 'cpunode29_bandwidth', 3), (57, 'cpunode29_processors', 4), (58, 'cpunode30_bandwidth', 3), (59, 'cpunode30_processors', 4), (60, 'cpunode31_bandwidth', 3), (61, 'cpunode31_processors', 4), (62, 'cpunode32_bandwidth', 3), (63, 'cpunode32_processors', 4), (64, 'cpunode33_bandwidth', 3), (65, 'cpunode33_processors', 4), (66, 'cpunode34_bandwidth', 3), (67, 'cpunode34_processors', 4), (68, 'cpunode35_bandwidth', 3), (69, 'cpunode35_processors', 4), (70, 'cpunode36_bandwidth', 3), (71, 'cpunode36_processors', 4), (72, 'cpunode37_bandwidth', 3), (73, 'cpunode37_processors', 4), (74, 'cpunode38_bandwidth', 3), (75, 'cpunode38_processors', 4), (76, 'cpunode39_bandwidth', 3), (77, 'cpunode39_processors', 4), (78, 'cpunode40_bandwidth', 3), (79, 'cpunode40_processors', 4), (80, 'cpunode41_bandwidth', 3), (81, 'cpunode41_processors', 4), (82, 'cpunode42_bandwidth', 3), (83, 'cpunode42_processors', 4), (84, 'cpunode43_bandwidth', 3), (85, 'cpunode43_processors', 4), (86, 'cpunode44_bandwidth', 3), (87, 'cpunode44_processors', 4), (88, 'cpunode45_bandwidth', 3), (89, 'cpunode45_processors', 4), (90, 'cpunode46_bandwidth', 3), (91, 'cpunode46_processors', 4), (92, 'cpunode47_bandwidth', 3), (93, 'cpunode47_processors', 4), (94, 'cpunode48_bandwidth', 3), (95, 'cpunode48_processors', 4), (96, 'cpunode49_bandwidth', 3), (97, 'cpunode49_processors', 4), (98, 'cpunode50_bandwidth', 3), (99, 'cpunode50_processors', 4), (100, 'cbt001_bandwidth', 3), (101, 'cbt001_processors', 4), (102, 'cbt002_bandwidth', 3), (103, 'cbt002_processors', 4), (104, 'cbt003_bandwidth', 3), (105, 'cbt003_processors', 4), (106, 'cbt004_bandwidth', 3), (107, 'cbt004_processors', 4), (108, 'cbt005_bandwidth', 3), (109, 'cbt005_processors', 4), (110, 'cbt006_bandwidth', 3), (111, 'cbt006_processors', 4), (112, 'cbt007_bandwidth', 3), (113, 'cbt007_processors', 4), (114, 'cbt008_bandwidth', 3), (115, 'cbt008_processors', 4), (116, 'CEP4_bandwidth:/data', 3), (117, 'CEP4_storage:/data', 5), (118, 'dragproc_bandwidth:/data', 3), (119, 'dragproc_storage:/data', 5), (120, 'drg01_bandwidth:/data1', 3), (121, 'drg01_bandwidth:/data2', 3), (122, 'drg01_storage:/data1', 5), (123, 'drg01_storage:/data2', 5), (124, 'drg02_bandwidth:/data1', 3), (125, 'drg02_bandwidth:/data2', 3), (126, 'drg02_storage:/data1', 5), (127, 'drg02_storage:/data2', 5), (128, 'drg03_bandwidth:/data1', 3), (129, 'drg03_bandwidth:/data2', 3), (130, 'drg03_storage:/data1', 5), (131, 'drg03_storage:/data2', 5), (132, 'drg04_bandwidth:/data1', 3), (133, 'drg04_bandwidth:/data2', 3), (134, 'drg04_storage:/data1', 5), (135, 'drg04_storage:/data2', 5), (136, 'drg05_bandwidth:/data1', 3), (137, 'drg05_bandwidth:/data2', 3), (138, 'drg05_storage:/data1', 5), (139, 'drg05_storage:/data2', 5), (140, 'drg06_bandwidth:/data1', 3), (141, 'drg06_bandwidth:/data2', 3), (142, 'drg06_storage:/data1', 5), (143, 'drg06_storage:/data2', 5), (144, 'drg07_bandwidth:/data1', 3), (145, 'drg07_bandwidth:/data2', 3), (146, 'drg07_storage:/data1', 5), (147, 'drg07_storage:/data2', 5), (148, 'drg08_bandwidth:/data1', 3), (149, 'drg08_bandwidth:/data2', 3), (150, 'drg08_storage:/data1', 5), (151, 'drg08_storage:/data2', 5), (152, 'drg09_bandwidth:/data1', 3), (153, 'drg09_bandwidth:/data2', 3), (154, 'drg09_storage:/data1', 5), (155, 'drg09_storage:/data2', 5), (156, 'drg10_bandwidth:/data1', 3), (157, 'drg10_bandwidth:/data2', 3), (158, 'drg10_storage:/data1', 5), (159, 'drg10_storage:/data2', 5), (160, 'drg11_bandwidth:/data1', 3), (161, 'drg11_bandwidth:/data2', 3), (162, 'drg11_storage:/data1', 5), (163, 'drg11_storage:/data2', 5), (164, 'drg12_bandwidth:/data1', 3), (165, 'drg12_bandwidth:/data2', 3), (166, 'drg12_storage:/data1', 5), (167, 'drg12_storage:/data2', 5), (168, 'drg13_bandwidth:/data1', 3), (169, 'drg13_bandwidth:/data2', 3), (170, 'drg13_storage:/data1', 5), (171, 'drg13_storage:/data2', 5), (172, 'drg14_bandwidth:/data1', 3), (173, 'drg14_bandwidth:/data2', 3), (174, 'drg14_storage:/data1', 5), (175, 'drg14_storage:/data2', 5), (176, 'drg15_bandwidth:/data1', 3), (177, 'drg15_bandwidth:/data2', 3), (178, 'drg15_storage:/data1', 5), (179, 'drg15_storage:/data2', 5), (180, 'drg16_bandwidth:/data1', 3), (181, 'drg16_bandwidth:/data2', 3), (182, 'drg16_storage:/data1', 5), (183, 'drg16_storage:/data2', 5), (184, 'drg17_bandwidth:/data1', 3), (185, 'drg17_bandwidth:/data2', 3), (186, 'drg17_storage:/data1', 5), (187, 'drg17_storage:/data2', 5), (188, 'drg18_bandwidth:/data1', 3), (189, 'drg18_bandwidth:/data2', 3), (190, 'drg18_storage:/data1', 5), (191, 'drg18_storage:/data2', 5), (192, 'drg19_bandwidth:/data1', 3), (193, 'drg19_bandwidth:/data2', 3), (194, 'drg19_storage:/data1', 5), (195, 'drg19_storage:/data2', 5), (196, 'drg20_bandwidth:/data1', 3), (197, 'drg20_bandwidth:/data2', 3), (198, 'drg20_storage:/data1', 5), (199, 'drg20_storage:/data2', 5), (200, 'drg21_bandwidth:/data1', 3), (201, 'drg21_bandwidth:/data2', 3), (202, 'drg21_storage:/data1', 5), (203, 'drg21_storage:/data2', 5), (204, 'drg22_bandwidth:/data1', 3), (205, 'drg22_bandwidth:/data2', 3), (206, 'drg22_storage:/data1', 5), (207, 'drg22_storage:/data2', 5), (208, 'drg23_bandwidth:/data1', 3), (209, 'drg23_bandwidth:/data2', 3), (210, 'drg23_storage:/data1', 5), (211, 'drg23_storage:/data2', 5), (212, 'CS001rcu', 2), (213, 'CS001tbb', 1), (214, 'CS002rcu', 2), (215, 'CS002tbb', 1), (216, 'CS003rcu', 2), (217, 'CS003tbb', 1), (218, 'CS004rcu', 2), (219, 'CS004tbb', 1), (220, 'CS005rcu', 2), (221, 'CS005tbb', 1), (222, 'CS006rcu', 2), (223, 'CS006tbb', 1), (224, 'CS007rcu', 2), (225, 'CS007tbb', 1), (226, 'CS011rcu', 2), (227, 'CS011tbb', 1), (228, 'CS013rcu', 2), (229, 'CS013tbb', 1), (230, 'CS017rcu', 2), (231, 'CS017tbb', 1), (232, 'CS021rcu', 2), (233, 'CS021tbb', 1), (234, 'CS024rcu', 2), (235, 'CS024tbb', 1), (236, 'CS026rcu', 2), (237, 'CS026tbb', 1), (238, 'CS028rcu', 2), (239, 'CS028tbb', 1), (240, 'CS030rcu', 2), (241, 'CS030tbb', 1), (242, 'CS031rcu', 2), (243, 'CS031tbb', 1), (244, 'CS032rcu', 2), (245, 'CS032tbb', 1), (246, 'CS101rcu', 2), (247, 'CS101tbb', 1), (248, 'CS103rcu', 2), (249, 'CS103tbb', 1), (250, 'CS201rcu', 2), (251, 'CS201tbb', 1), (252, 'CS301rcu', 2), (253, 'CS301tbb', 1), (254, 'CS302rcu', 2), (255, 'CS302tbb', 1), (256, 'CS401rcu', 2), (257, 'CS401tbb', 1), (258, 'CS501rcu', 2), (259, 'CS501tbb', 1), (260, 'RS106rcu', 2), (261, 'RS106tbb', 1), (262, 'RS205rcu', 2), (263, 'RS205tbb', 1), (264, 'RS208rcu', 2), (265, 'RS208tbb', 1), (266, 'RS210rcu', 2), (267, 'RS210tbb', 1), (268, 'RS305rcu', 2), (269, 'RS305tbb', 1), (270, 'RS306rcu', 2), (271, 'RS306tbb', 1), (272, 'RS307rcu', 2), (273, 'RS307tbb', 1), (274, 'RS310rcu', 2), (275, 'RS310tbb', 1), (276, 'RS406rcu', 2), (277, 'RS406tbb', 1), (278, 'RS407rcu', 2), (279, 'RS407tbb', 1), (280, 'RS408rcu', 2), (281, 'RS408tbb', 1), (282, 'RS409rcu', 2), (283, 'RS409tbb', 1), (284, 'RS503rcu', 2), (285, 'RS503tbb', 1), (286, 'RS508rcu', 2), (287, 'RS508tbb', 1), (288, 'RS509rcu', 2), (289, 'RS509tbb', 1), (290, 'DE601rcu', 2), (291, 'DE601tbb', 1), (292, 'DE602rcu', 2), (293, 'DE602tbb', 1), (294, 'DE603rcu', 2), (295, 'DE603tbb', 1), (296, 'DE604rcu', 2), (297, 'DE604tbb', 1), (298, 'DE605rcu', 2), (299, 'DE605tbb', 1), (300, 'FR606rcu', 2), (301, 'FR606tbb', 1), (302, 'SE607rcu', 2), (303, 'SE607tbb', 1), (304, 'UK608rcu', 2), (305, 'UK608tbb', 1), (306, 'DE609rcu', 2), (307, 'DE609tbb', 1), (308, 'PL610rcu', 2), (309, 'PL610tbb', 1), (310, 'PL611rcu', 2), (311, 'PL611tbb', 1), (312, 'PL612rcu', 2), (313, 'PL612tbb', 1), (314, 'IE613rcu', 2), (315, 'IE613tbb', 1), (316, 'IS614rcu', 2), (317, 'IS614tbb', 1), (318, 'TEST1rcu', 2), (319, 'TEST1tbb', 1), (320, 'CS001chan0', 0), (321, 'CS001bw0', 3), (322, 'CS001chan1', 0), (323, 'CS001bw1', 3), (324, 'CS002chan0', 0), (325, 'CS002bw0', 3), (326, 'CS002chan1', 0), (327, 'CS002bw1', 3), (328, 'CS003chan0', 0), (329, 'CS003bw0', 3), (330, 'CS003chan1', 0), (331, 'CS003bw1', 3), (332, 'CS004chan0', 0), (333, 'CS004bw0', 3), (334, 'CS004chan1', 0), (335, 'CS004bw1', 3), (336, 'CS005chan0', 0), (337, 'CS005bw0', 3), (338, 'CS005chan1', 0), (339, 'CS005bw1', 3), (340, 'CS006chan0', 0), (341, 'CS006bw0', 3), (342, 'CS006chan1', 0), (343, 'CS006bw1', 3), (344, 'CS007chan0', 0), (345, 'CS007bw0', 3), (346, 'CS007chan1', 0), (347, 'CS007bw1', 3), (348, 'CS011chan0', 0), (349, 'CS011bw0', 3), (350, 'CS011chan1', 0), (351, 'CS011bw1', 3), (352, 'CS013chan0', 0), (353, 'CS013bw0', 3), (354, 'CS013chan1', 0), (355, 'CS013bw1', 3), (356, 'CS017chan0', 0), (357, 'CS017bw0', 3), (358, 'CS017chan1', 0), (359, 'CS017bw1', 3), (360, 'CS021chan0', 0), (361, 'CS021bw0', 3), (362, 'CS021chan1', 0), (363, 'CS021bw1', 3), (364, 'CS024chan0', 0), (365, 'CS024bw0', 3), (366, 'CS024chan1', 0), (367, 'CS024bw1', 3), (368, 'CS026chan0', 0), (369, 'CS026bw0', 3), (370, 'CS026chan1', 0), (371, 'CS026bw1', 3), (372, 'CS028chan0', 0), (373, 'CS028bw0', 3), (374, 'CS028chan1', 0), (375, 'CS028bw1', 3), (376, 'CS030chan0', 0), (377, 'CS030bw0', 3), (378, 'CS030chan1', 0), (379, 'CS030bw1', 3), (380, 'CS031chan0', 0), (381, 'CS031bw0', 3), (382, 'CS031chan1', 0), (383, 'CS031bw1', 3), (384, 'CS032chan0', 0), (385, 'CS032bw0', 3), (386, 'CS032chan1', 0), (387, 'CS032bw1', 3), (388, 'CS101chan0', 0), (389, 'CS101bw0', 3), (390, 'CS101chan1', 0), (391, 'CS101bw1', 3), (392, 'CS103chan0', 0), (393, 'CS103bw0', 3), (394, 'CS103chan1', 0), (395, 'CS103bw1', 3), (396, 'CS201chan0', 0), (397, 'CS201bw0', 3), (398, 'CS201chan1', 0), (399, 'CS201bw1', 3), (400, 'CS301chan0', 0), (401, 'CS301bw0', 3), (402, 'CS301chan1', 0), (403, 'CS301bw1', 3), (404, 'CS302chan0', 0), (405, 'CS302bw0', 3), (406, 'CS302chan1', 0), (407, 'CS302bw1', 3), (408, 'CS401chan0', 0), (409, 'CS401bw0', 3), (410, 'CS401chan1', 0), (411, 'CS401bw1', 3), (412, 'CS501chan0', 0), (413, 'CS501bw0', 3), (414, 'CS501chan1', 0), (415, 'CS501bw1', 3), (416, 'RS106chan', 0), (417, 'RS106bw', 3), (418, 'RS205chan', 0), (419, 'RS205bw', 3), (420, 'RS208chan', 0), (421, 'RS208bw', 3), (422, 'RS210chan', 0), (423, 'RS210bw', 3), (424, 'RS305chan', 0), (425, 'RS305bw', 3), (426, 'RS306chan', 0), (427, 'RS306bw', 3), (428, 'RS307chan', 0), (429, 'RS307bw', 3), (430, 'RS310chan', 0), (431, 'RS310bw', 3), (432, 'RS406chan', 0), (433, 'RS406bw', 3), (434, 'RS407chan', 0), (435, 'RS407bw', 3), (436, 'RS408chan', 0), (437, 'RS408bw', 3), (438, 'RS409chan', 0), (439, 'RS409bw', 3), (440, 'RS503chan', 0), (441, 'RS503bw', 3), (442, 'RS508chan', 0), (443, 'RS508bw', 3), (444, 'RS509chan', 0), (445, 'RS509bw', 3), (446, 'DE601chan', 0), (447, 'DE601bw', 3), (448, 'DE602chan', 0), (449, 'DE602bw', 3), (450, 'DE603chan', 0), (451, 'DE603bw', 3), (452, 'DE604chan', 0), (453, 'DE604bw', 3), (454, 'DE605chan', 0), (455, 'DE605bw', 3), (456, 'FR606chan', 0), (457, 'FR606bw', 3), (458, 'SE607chan', 0), (459, 'SE607bw', 3), (460, 'UK608chan', 0), (461, 'UK608bw', 3), (462, 'DE609chan', 0), (463, 'DE609bw', 3), (464, 'PL610chan', 0), (465, 'PL610bw', 3), (466, 'PL611chan', 0), (467, 'PL611bw', 3), (468, 'PL612chan', 0), (469, 'PL612bw', 3), (470, 'IE613chan', 0), (471, 'IE613bw', 3), (472, 'IS614chan', 0), (473, 'IS614bw', 3), (474, 'TEST1chan', 0), (475, 'TEST1bw', 3); INSERT INTO virtual_instrument.resource_to_resource_group VALUES (DEFAULT, 0, 5), (DEFAULT, 1, 5), (DEFAULT, 2, 6), (DEFAULT, 3, 6), (DEFAULT, 4, 7), (DEFAULT, 5, 7), (DEFAULT, 6, 8), (DEFAULT, 7, 8), (DEFAULT, 8, 9), (DEFAULT, 9, 9), (DEFAULT, 10, 10), (DEFAULT, 11, 10), (DEFAULT, 12, 11), (DEFAULT, 13, 11), (DEFAULT, 14, 12), (DEFAULT, 15, 12), (DEFAULT, 16, 13), (DEFAULT, 17, 13), (DEFAULT, 18, 14), (DEFAULT, 19, 14), (DEFAULT, 20, 15), (DEFAULT, 21, 15), (DEFAULT, 22, 16), (DEFAULT, 23, 16), (DEFAULT, 24, 17), (DEFAULT, 25, 17), (DEFAULT, 26, 18), (DEFAULT, 27, 18), (DEFAULT, 28, 19), (DEFAULT, 29, 19), (DEFAULT, 30, 20), (DEFAULT, 31, 20), (DEFAULT, 32, 21), (DEFAULT, 33, 21), (DEFAULT, 34, 22), (DEFAULT, 35, 22), (DEFAULT, 36, 23), (DEFAULT, 37, 23), (DEFAULT, 38, 24), (DEFAULT, 39, 24), (DEFAULT, 40, 25), (DEFAULT, 41, 25), (DEFAULT, 42, 26), (DEFAULT, 43, 26), (DEFAULT, 44, 27), (DEFAULT, 45, 27), (DEFAULT, 46, 28), (DEFAULT, 47, 28), (DEFAULT, 48, 29), (DEFAULT, 49, 29), (DEFAULT, 50, 30), (DEFAULT, 51, 30), (DEFAULT, 52, 31), (DEFAULT, 53, 31), (DEFAULT, 54, 32), (DEFAULT, 55, 32), (DEFAULT, 56, 33), (DEFAULT, 57, 33), (DEFAULT, 58, 34), (DEFAULT, 59, 34), (DEFAULT, 60, 35), (DEFAULT, 61, 35), (DEFAULT, 62, 36), (DEFAULT, 63, 36), (DEFAULT, 64, 37), (DEFAULT, 65, 37), (DEFAULT, 66, 38), (DEFAULT, 67, 38), (DEFAULT, 68, 39), (DEFAULT, 69, 39), (DEFAULT, 70, 40), (DEFAULT, 71, 40), (DEFAULT, 72, 41), (DEFAULT, 73, 41), (DEFAULT, 74, 42), (DEFAULT, 75, 42), (DEFAULT, 76, 43), (DEFAULT, 77, 43), (DEFAULT, 78, 44), (DEFAULT, 79, 44), (DEFAULT, 80, 45), (DEFAULT, 81, 45), (DEFAULT, 82, 46), (DEFAULT, 83, 46), (DEFAULT, 84, 47), (DEFAULT, 85, 47), (DEFAULT, 86, 48), (DEFAULT, 87, 48), (DEFAULT, 88, 49), (DEFAULT, 89, 49), (DEFAULT, 90, 50), (DEFAULT, 91, 50), (DEFAULT, 92, 51), (DEFAULT, 93, 51), (DEFAULT, 94, 52), (DEFAULT, 95, 52), (DEFAULT, 96, 53), (DEFAULT, 97, 53), (DEFAULT, 98, 54), (DEFAULT, 99, 54), (DEFAULT, 100, 55), (DEFAULT, 101, 55), (DEFAULT, 102, 56), (DEFAULT, 103, 56), (DEFAULT, 104, 57), (DEFAULT, 105, 57), (DEFAULT, 106, 58), (DEFAULT, 107, 58), (DEFAULT, 108, 59), (DEFAULT, 109, 59), (DEFAULT, 110, 60), (DEFAULT, 111, 60), (DEFAULT, 112, 61), (DEFAULT, 113, 61), (DEFAULT, 114, 62), (DEFAULT, 115, 62), (DEFAULT, 116, 1), (DEFAULT, 117, 1), (DEFAULT, 118, 65), (DEFAULT, 119, 65), (DEFAULT, 120, 67), (DEFAULT, 121, 68), (DEFAULT, 122, 67), (DEFAULT, 123, 68), (DEFAULT, 124, 70), (DEFAULT, 125, 71), (DEFAULT, 126, 70), (DEFAULT, 127, 71), (DEFAULT, 128, 73), (DEFAULT, 129, 74), (DEFAULT, 130, 73), (DEFAULT, 131, 74), (DEFAULT, 132, 76), (DEFAULT, 133, 77), (DEFAULT, 134, 76), (DEFAULT, 135, 77), (DEFAULT, 136, 79), (DEFAULT, 137, 80), (DEFAULT, 138, 79), (DEFAULT, 139, 80), (DEFAULT, 140, 82), (DEFAULT, 141, 83), (DEFAULT, 142, 82), (DEFAULT, 143, 83), (DEFAULT, 144, 85), (DEFAULT, 145, 86), (DEFAULT, 146, 85), (DEFAULT, 147, 86), (DEFAULT, 148, 88), (DEFAULT, 149, 89), (DEFAULT, 150, 88), (DEFAULT, 151, 89), (DEFAULT, 152, 91), (DEFAULT, 153, 92), (DEFAULT, 154, 91), (DEFAULT, 155, 92), (DEFAULT, 156, 94), (DEFAULT, 157, 95), (DEFAULT, 158, 94), (DEFAULT, 159, 95), (DEFAULT, 160, 97), (DEFAULT, 161, 98), (DEFAULT, 162, 97), (DEFAULT, 163, 98), (DEFAULT, 164, 100), (DEFAULT, 165, 101), (DEFAULT, 166, 100), (DEFAULT, 167, 101), (DEFAULT, 168, 103), (DEFAULT, 169, 104), (DEFAULT, 170, 103), (DEFAULT, 171, 104), (DEFAULT, 172, 106), (DEFAULT, 173, 107), (DEFAULT, 174, 106), (DEFAULT, 175, 107), (DEFAULT, 176, 109), (DEFAULT, 177, 110), (DEFAULT, 178, 109), (DEFAULT, 179, 110), (DEFAULT, 180, 112), (DEFAULT, 181, 113), (DEFAULT, 182, 112), (DEFAULT, 183, 113), (DEFAULT, 184, 115), (DEFAULT, 185, 116), (DEFAULT, 186, 115), (DEFAULT, 187, 116), (DEFAULT, 188, 118), (DEFAULT, 189, 119), (DEFAULT, 190, 118), (DEFAULT, 191, 119), (DEFAULT, 192, 121), (DEFAULT, 193, 122), (DEFAULT, 194, 121), (DEFAULT, 195, 122), (DEFAULT, 196, 124), (DEFAULT, 197, 125), (DEFAULT, 198, 124), (DEFAULT, 199, 125), (DEFAULT, 200, 127), (DEFAULT, 201, 128), (DEFAULT, 202, 127), (DEFAULT, 203, 128), (DEFAULT, 204, 130), (DEFAULT, 205, 131), (DEFAULT, 206, 130), (DEFAULT, 207, 131), (DEFAULT, 208, 133), (DEFAULT, 209, 134), (DEFAULT, 210, 133), (DEFAULT, 211, 134), (DEFAULT, 212, 149), (DEFAULT, 213, 149), (DEFAULT, 214, 150), (DEFAULT, 215, 150), (DEFAULT, 216, 151), (DEFAULT, 217, 151), (DEFAULT, 218, 152), (DEFAULT, 219, 152), (DEFAULT, 220, 153), (DEFAULT, 221, 153), (DEFAULT, 222, 154), (DEFAULT, 223, 154), (DEFAULT, 224, 155), (DEFAULT, 225, 155), (DEFAULT, 226, 156), (DEFAULT, 227, 156), (DEFAULT, 228, 157), (DEFAULT, 229, 157), (DEFAULT, 230, 158), (DEFAULT, 231, 158), (DEFAULT, 232, 159), (DEFAULT, 233, 159), (DEFAULT, 234, 160), (DEFAULT, 235, 160), (DEFAULT, 236, 161), (DEFAULT, 237, 161), (DEFAULT, 238, 162), (DEFAULT, 239, 162), (DEFAULT, 240, 163), (DEFAULT, 241, 163), (DEFAULT, 242, 164), (DEFAULT, 243, 164), (DEFAULT, 244, 165), (DEFAULT, 245, 165), (DEFAULT, 246, 166), (DEFAULT, 247, 166), (DEFAULT, 248, 167), (DEFAULT, 249, 167), (DEFAULT, 250, 168), (DEFAULT, 251, 168), (DEFAULT, 252, 169), (DEFAULT, 253, 169), (DEFAULT, 254, 170), (DEFAULT, 255, 170), (DEFAULT, 256, 171), (DEFAULT, 257, 171), (DEFAULT, 258, 172), (DEFAULT, 259, 172), (DEFAULT, 260, 173), (DEFAULT, 261, 173), (DEFAULT, 262, 174), (DEFAULT, 263, 174), (DEFAULT, 264, 175), (DEFAULT, 265, 175), (DEFAULT, 266, 176), (DEFAULT, 267, 176), (DEFAULT, 268, 177), (DEFAULT, 269, 177), (DEFAULT, 270, 178), (DEFAULT, 271, 178), (DEFAULT, 272, 179), (DEFAULT, 273, 179), (DEFAULT, 274, 180), (DEFAULT, 275, 180), (DEFAULT, 276, 181), (DEFAULT, 277, 181), (DEFAULT, 278, 182), (DEFAULT, 279, 182), (DEFAULT, 280, 183), (DEFAULT, 281, 183), (DEFAULT, 282, 184), (DEFAULT, 283, 184), (DEFAULT, 284, 185), (DEFAULT, 285, 185), (DEFAULT, 286, 186), (DEFAULT, 287, 186), (DEFAULT, 288, 187), (DEFAULT, 289, 187), (DEFAULT, 290, 188), (DEFAULT, 291, 188), (DEFAULT, 292, 189), (DEFAULT, 293, 189), (DEFAULT, 294, 190), (DEFAULT, 295, 190), (DEFAULT, 296, 191), (DEFAULT, 297, 191), (DEFAULT, 298, 192), (DEFAULT, 299, 192), (DEFAULT, 300, 193), (DEFAULT, 301, 193), (DEFAULT, 302, 194), (DEFAULT, 303, 194), (DEFAULT, 304, 195), (DEFAULT, 305, 195), (DEFAULT, 306, 196), (DEFAULT, 307, 196), (DEFAULT, 308, 197), (DEFAULT, 309, 197), (DEFAULT, 310, 198), (DEFAULT, 311, 198), (DEFAULT, 312, 199), (DEFAULT, 313, 199), (DEFAULT, 314, 200), (DEFAULT, 315, 200), (DEFAULT, 316, 201), (DEFAULT, 317, 201), (DEFAULT, 318, 202), (DEFAULT, 319, 202), (DEFAULT, 320, 206), (DEFAULT, 321, 206), (DEFAULT, 322, 207), (DEFAULT, 323, 207), (DEFAULT, 324, 208), (DEFAULT, 325, 208), (DEFAULT, 326, 209), (DEFAULT, 327, 209), (DEFAULT, 328, 210), (DEFAULT, 329, 210), (DEFAULT, 330, 211), (DEFAULT, 331, 211), (DEFAULT, 332, 212), (DEFAULT, 333, 212), (DEFAULT, 334, 213), (DEFAULT, 335, 213), (DEFAULT, 336, 214), (DEFAULT, 337, 214), (DEFAULT, 338, 215), (DEFAULT, 339, 215), (DEFAULT, 340, 216), (DEFAULT, 341, 216), (DEFAULT, 342, 217), (DEFAULT, 343, 217), (DEFAULT, 344, 218), (DEFAULT, 345, 218), (DEFAULT, 346, 219), (DEFAULT, 347, 219), (DEFAULT, 348, 220), (DEFAULT, 349, 220), (DEFAULT, 350, 221), (DEFAULT, 351, 221), (DEFAULT, 352, 222), (DEFAULT, 353, 222), (DEFAULT, 354, 223), (DEFAULT, 355, 223), (DEFAULT, 356, 224), (DEFAULT, 357, 224), (DEFAULT, 358, 225), (DEFAULT, 359, 225), (DEFAULT, 360, 226), (DEFAULT, 361, 226), (DEFAULT, 362, 227), (DEFAULT, 363, 227), (DEFAULT, 364, 228), (DEFAULT, 365, 228), (DEFAULT, 366, 229), (DEFAULT, 367, 229), (DEFAULT, 368, 230), (DEFAULT, 369, 230), (DEFAULT, 370, 231), (DEFAULT, 371, 231), (DEFAULT, 372, 232), (DEFAULT, 373, 232), (DEFAULT, 374, 233), (DEFAULT, 375, 233), (DEFAULT, 376, 234), (DEFAULT, 377, 234), (DEFAULT, 378, 235), (DEFAULT, 379, 235), (DEFAULT, 380, 236), (DEFAULT, 381, 236), (DEFAULT, 382, 237), (DEFAULT, 383, 237), (DEFAULT, 384, 238), (DEFAULT, 385, 238), (DEFAULT, 386, 239), (DEFAULT, 387, 239), (DEFAULT, 388, 240), (DEFAULT, 389, 240), (DEFAULT, 390, 241), (DEFAULT, 391, 241), (DEFAULT, 392, 242), (DEFAULT, 393, 242), (DEFAULT, 394, 243), (DEFAULT, 395, 243), (DEFAULT, 396, 244), (DEFAULT, 397, 244), (DEFAULT, 398, 245), (DEFAULT, 399, 245), (DEFAULT, 400, 246), (DEFAULT, 401, 246), (DEFAULT, 402, 247), (DEFAULT, 403, 247), (DEFAULT, 404, 248), (DEFAULT, 405, 248), (DEFAULT, 406, 249), (DEFAULT, 407, 249), (DEFAULT, 408, 250), (DEFAULT, 409, 250), (DEFAULT, 410, 251), (DEFAULT, 411, 251), (DEFAULT, 412, 252), (DEFAULT, 413, 252), (DEFAULT, 414, 253), (DEFAULT, 415, 253), (DEFAULT, 416, 254), (DEFAULT, 417, 254), (DEFAULT, 418, 255), (DEFAULT, 419, 255), (DEFAULT, 420, 256), (DEFAULT, 421, 256), (DEFAULT, 422, 257), (DEFAULT, 423, 257), (DEFAULT, 424, 258), (DEFAULT, 425, 258), (DEFAULT, 426, 259), (DEFAULT, 427, 259), (DEFAULT, 428, 260), (DEFAULT, 429, 260), (DEFAULT, 430, 261), (DEFAULT, 431, 261), (DEFAULT, 432, 262), (DEFAULT, 433, 262), (DEFAULT, 434, 263), (DEFAULT, 435, 263), (DEFAULT, 436, 264), (DEFAULT, 437, 264), (DEFAULT, 438, 265), (DEFAULT, 439, 265), (DEFAULT, 440, 266), (DEFAULT, 441, 266), (DEFAULT, 442, 267), (DEFAULT, 443, 267), (DEFAULT, 444, 268), (DEFAULT, 445, 268), (DEFAULT, 446, 269), (DEFAULT, 447, 269), (DEFAULT, 448, 270), (DEFAULT, 449, 270), (DEFAULT, 450, 271), (DEFAULT, 451, 271), (DEFAULT, 452, 272), (DEFAULT, 453, 272), (DEFAULT, 454, 273), (DEFAULT, 455, 273), (DEFAULT, 456, 274), (DEFAULT, 457, 274), (DEFAULT, 458, 275), (DEFAULT, 459, 275), (DEFAULT, 460, 276), (DEFAULT, 461, 276), (DEFAULT, 462, 277), (DEFAULT, 463, 277), (DEFAULT, 464, 278), (DEFAULT, 465, 278), (DEFAULT, 466, 279), (DEFAULT, 467, 279), (DEFAULT, 468, 280), (DEFAULT, 469, 280), (DEFAULT, 470, 281), (DEFAULT, 471, 281), (DEFAULT, 472, 282), (DEFAULT, 473, 282), (DEFAULT, 474, 283), (DEFAULT, 475, 283); -INSERT INTO resource_monitoring.resource_capacity VALUES (DEFAULT, 0, 26000000000, 26000000000), (DEFAULT, 1, 24, 24), (DEFAULT, 2, 26000000000, 26000000000), (DEFAULT, 3, 24, 24), (DEFAULT, 4, 26000000000, 26000000000), (DEFAULT, 5, 24, 24), (DEFAULT, 6, 26000000000, 26000000000), (DEFAULT, 7, 24, 24), (DEFAULT, 8, 26000000000, 26000000000), (DEFAULT, 9, 24, 24), (DEFAULT, 10, 26000000000, 26000000000), (DEFAULT, 11, 24, 24), (DEFAULT, 12, 26000000000, 26000000000), (DEFAULT, 13, 24, 24), (DEFAULT, 14, 26000000000, 26000000000), (DEFAULT, 15, 24, 24), (DEFAULT, 16, 26000000000, 26000000000), (DEFAULT, 17, 24, 24), (DEFAULT, 18, 26000000000, 26000000000), (DEFAULT, 19, 24, 24), (DEFAULT, 20, 26000000000, 26000000000), (DEFAULT, 21, 24, 24), (DEFAULT, 22, 26000000000, 26000000000), (DEFAULT, 23, 24, 24), (DEFAULT, 24, 26000000000, 26000000000), (DEFAULT, 25, 24, 24), (DEFAULT, 26, 26000000000, 26000000000), (DEFAULT, 27, 24, 24), (DEFAULT, 28, 26000000000, 26000000000), (DEFAULT, 29, 24, 24), (DEFAULT, 30, 26000000000, 26000000000), (DEFAULT, 31, 24, 24), (DEFAULT, 32, 26000000000, 26000000000), (DEFAULT, 33, 24, 24), (DEFAULT, 34, 26000000000, 26000000000), (DEFAULT, 35, 24, 24), (DEFAULT, 36, 26000000000, 26000000000), (DEFAULT, 37, 24, 24), (DEFAULT, 38, 26000000000, 26000000000), (DEFAULT, 39, 24, 24), (DEFAULT, 40, 26000000000, 26000000000), (DEFAULT, 41, 24, 24), (DEFAULT, 42, 26000000000, 26000000000), (DEFAULT, 43, 24, 24), (DEFAULT, 44, 26000000000, 26000000000), (DEFAULT, 45, 24, 24), (DEFAULT, 46, 26000000000, 26000000000), (DEFAULT, 47, 24, 24), (DEFAULT, 48, 26000000000, 26000000000), (DEFAULT, 49, 24, 24), (DEFAULT, 50, 26000000000, 26000000000), (DEFAULT, 51, 24, 24), (DEFAULT, 52, 26000000000, 26000000000), (DEFAULT, 53, 24, 24), (DEFAULT, 54, 26000000000, 26000000000), (DEFAULT, 55, 24, 24), (DEFAULT, 56, 26000000000, 26000000000), (DEFAULT, 57, 24, 24), (DEFAULT, 58, 26000000000, 26000000000), (DEFAULT, 59, 24, 24), (DEFAULT, 60, 26000000000, 26000000000), (DEFAULT, 61, 24, 24), (DEFAULT, 62, 26000000000, 26000000000), (DEFAULT, 63, 24, 24), (DEFAULT, 64, 26000000000, 26000000000), (DEFAULT, 65, 24, 24), (DEFAULT, 66, 26000000000, 26000000000), (DEFAULT, 67, 24, 24), (DEFAULT, 68, 26000000000, 26000000000), (DEFAULT, 69, 24, 24), (DEFAULT, 70, 26000000000, 26000000000), (DEFAULT, 71, 24, 24), (DEFAULT, 72, 26000000000, 26000000000), (DEFAULT, 73, 24, 24), (DEFAULT, 74, 26000000000, 26000000000), (DEFAULT, 75, 24, 24), (DEFAULT, 76, 26000000000, 26000000000), (DEFAULT, 77, 24, 24), (DEFAULT, 78, 26000000000, 26000000000), (DEFAULT, 79, 24, 24), (DEFAULT, 80, 26000000000, 26000000000), (DEFAULT, 81, 24, 24), (DEFAULT, 82, 26000000000, 26000000000), (DEFAULT, 83, 24, 24), (DEFAULT, 84, 26000000000, 26000000000), (DEFAULT, 85, 24, 24), (DEFAULT, 86, 26000000000, 26000000000), (DEFAULT, 87, 24, 24), (DEFAULT, 88, 26000000000, 26000000000), (DEFAULT, 89, 24, 24), (DEFAULT, 90, 26000000000, 26000000000), (DEFAULT, 91, 24, 24), (DEFAULT, 92, 26000000000, 26000000000), (DEFAULT, 93, 24, 24), (DEFAULT, 94, 26000000000, 26000000000), (DEFAULT, 95, 24, 24), (DEFAULT, 96, 26000000000, 26000000000), (DEFAULT, 97, 24, 24), (DEFAULT, 98, 26000000000, 26000000000), (DEFAULT, 99, 24, 24), (DEFAULT, 100, 52000000000, 52000000000), (DEFAULT, 101, 24, 24), (DEFAULT, 102, 52000000000, 52000000000), (DEFAULT, 103, 24, 24), (DEFAULT, 104, 52000000000, 52000000000), (DEFAULT, 105, 24, 24), (DEFAULT, 106, 52000000000, 52000000000), (DEFAULT, 107, 24, 24), (DEFAULT, 108, 52000000000, 52000000000), (DEFAULT, 109, 24, 24), (DEFAULT, 110, 52000000000, 52000000000), (DEFAULT, 111, 24, 24), (DEFAULT, 112, 52000000000, 52000000000), (DEFAULT, 113, 24, 24), (DEFAULT, 114, 52000000000, 52000000000), (DEFAULT, 115, 24, 24), (DEFAULT, 116, 687194767360, 687194767360), (DEFAULT, 117, 3450434462023680, 3450434462023680), (DEFAULT, 118, 3774873600, 3774873600), (DEFAULT, 119, 23669957984256, 23669957984256), (DEFAULT, 120, 2030043136, 2030043136), (DEFAULT, 121, 2030043136, 2030043136), (DEFAULT, 122, 7913168961536, 7913168961536), (DEFAULT, 123, 7913168961536, 7913168961536), (DEFAULT, 124, 2030043136, 2030043136), (DEFAULT, 125, 2030043136, 2030043136), (DEFAULT, 126, 7913168961536, 7913168961536), (DEFAULT, 127, 7913168961536, 7913168961536), (DEFAULT, 128, 2030043136, 2030043136), (DEFAULT, 129, 2030043136, 2030043136), (DEFAULT, 130, 7913168961536, 7913168961536), (DEFAULT, 131, 7913168961536, 7913168961536), (DEFAULT, 132, 2030043136, 2030043136), (DEFAULT, 133, 2030043136, 2030043136), (DEFAULT, 134, 7913168961536, 7913168961536), (DEFAULT, 135, 7913168961536, 7913168961536), (DEFAULT, 136, 2030043136, 2030043136), (DEFAULT, 137, 2030043136, 2030043136), (DEFAULT, 138, 7913168961536, 7913168961536), (DEFAULT, 139, 7913168961536, 7913168961536), (DEFAULT, 140, 2030043136, 2030043136), (DEFAULT, 141, 2030043136, 2030043136), (DEFAULT, 142, 7913168961536, 7913168961536), (DEFAULT, 143, 7913168961536, 7913168961536), (DEFAULT, 144, 2030043136, 2030043136), (DEFAULT, 145, 2030043136, 2030043136), (DEFAULT, 146, 7913168961536, 7913168961536), (DEFAULT, 147, 7913168961536, 7913168961536), (DEFAULT, 148, 2030043136, 2030043136), (DEFAULT, 149, 2030043136, 2030043136), (DEFAULT, 150, 7913168961536, 7913168961536), (DEFAULT, 151, 7913168961536, 7913168961536), (DEFAULT, 152, 2030043136, 2030043136), (DEFAULT, 153, 2030043136, 2030043136), (DEFAULT, 154, 7913168961536, 7913168961536), (DEFAULT, 155, 7913168961536, 7913168961536), (DEFAULT, 156, 2030043136, 2030043136), (DEFAULT, 157, 2030043136, 2030043136), (DEFAULT, 158, 7913168961536, 7913168961536), (DEFAULT, 159, 7913168961536, 7913168961536), (DEFAULT, 160, 2030043136, 2030043136), (DEFAULT, 161, 2030043136, 2030043136), (DEFAULT, 162, 7913168961536, 7913168961536), (DEFAULT, 163, 7913168961536, 7913168961536), (DEFAULT, 164, 2030043136, 2030043136), (DEFAULT, 165, 2030043136, 2030043136), (DEFAULT, 166, 7913168961536, 7913168961536), (DEFAULT, 167, 7913168961536, 7913168961536), (DEFAULT, 168, 2030043136, 2030043136), (DEFAULT, 169, 2030043136, 2030043136), (DEFAULT, 170, 7913168961536, 7913168961536), (DEFAULT, 171, 7913168961536, 7913168961536), (DEFAULT, 172, 2030043136, 2030043136), (DEFAULT, 173, 2030043136, 2030043136), (DEFAULT, 174, 7913168961536, 7913168961536), (DEFAULT, 175, 7913168961536, 7913168961536), (DEFAULT, 176, 2030043136, 2030043136), (DEFAULT, 177, 2030043136, 2030043136), (DEFAULT, 178, 7913168961536, 7913168961536), (DEFAULT, 179, 7913168961536, 7913168961536), (DEFAULT, 180, 2030043136, 2030043136), (DEFAULT, 181, 2030043136, 2030043136), (DEFAULT, 182, 7913168961536, 7913168961536), (DEFAULT, 183, 7913168961536, 7913168961536), (DEFAULT, 184, 2030043136, 2030043136), (DEFAULT, 185, 2030043136, 2030043136), (DEFAULT, 186, 7913168961536, 7913168961536), (DEFAULT, 187, 7913168961536, 7913168961536), (DEFAULT, 188, 2030043136, 2030043136), (DEFAULT, 189, 2030043136, 2030043136), (DEFAULT, 190, 7913168961536, 7913168961536), (DEFAULT, 191, 7913168961536, 7913168961536), (DEFAULT, 192, 2030043136, 2030043136), (DEFAULT, 193, 2030043136, 2030043136), (DEFAULT, 194, 7913168961536, 7913168961536), (DEFAULT, 195, 7913168961536, 7913168961536), (DEFAULT, 196, 2030043136, 2030043136), (DEFAULT, 197, 2030043136, 2030043136), (DEFAULT, 198, 7913168961536, 7913168961536), (DEFAULT, 199, 7913168961536, 7913168961536), (DEFAULT, 200, 2030043136, 2030043136), (DEFAULT, 201, 2030043136, 2030043136), (DEFAULT, 202, 7913168961536, 7913168961536), (DEFAULT, 203, 7913168961536, 7913168961536), (DEFAULT, 204, 2030043136, 2030043136), (DEFAULT, 205, 2030043136, 2030043136), (DEFAULT, 206, 7913168961536, 7913168961536), (DEFAULT, 207, 7913168961536, 7913168961536), (DEFAULT, 208, 2030043136, 2030043136), (DEFAULT, 209, 2030043136, 2030043136), (DEFAULT, 210, 7913168961536, 7913168961536), (DEFAULT, 211, 7913168961536, 7913168961536), (DEFAULT, 212, 96, 96), (DEFAULT, 213, 824633720832, 824633720832), (DEFAULT, 214, 96, 96), (DEFAULT, 215, 824633720832, 824633720832), (DEFAULT, 216, 96, 96), (DEFAULT, 217, 824633720832, 824633720832), (DEFAULT, 218, 96, 96), (DEFAULT, 219, 824633720832, 824633720832), (DEFAULT, 220, 96, 96), (DEFAULT, 221, 824633720832, 824633720832), (DEFAULT, 222, 96, 96), (DEFAULT, 223, 824633720832, 824633720832), (DEFAULT, 224, 96, 96), (DEFAULT, 225, 824633720832, 824633720832), (DEFAULT, 226, 96, 96), (DEFAULT, 227, 824633720832, 824633720832), (DEFAULT, 228, 96, 96), (DEFAULT, 229, 824633720832, 824633720832), (DEFAULT, 230, 96, 96), (DEFAULT, 231, 824633720832, 824633720832), (DEFAULT, 232, 96, 96), (DEFAULT, 233, 824633720832, 824633720832), (DEFAULT, 234, 96, 96), (DEFAULT, 235, 824633720832, 824633720832), (DEFAULT, 236, 96, 96), (DEFAULT, 237, 824633720832, 824633720832), (DEFAULT, 238, 96, 96), (DEFAULT, 239, 824633720832, 824633720832), (DEFAULT, 240, 96, 96), (DEFAULT, 241, 824633720832, 824633720832), (DEFAULT, 242, 96, 96), (DEFAULT, 243, 824633720832, 824633720832), (DEFAULT, 244, 96, 96), (DEFAULT, 245, 824633720832, 824633720832), (DEFAULT, 246, 96, 96), (DEFAULT, 247, 824633720832, 824633720832), (DEFAULT, 248, 96, 96), (DEFAULT, 249, 824633720832, 824633720832), (DEFAULT, 250, 96, 96), (DEFAULT, 251, 824633720832, 824633720832), (DEFAULT, 252, 96, 96), (DEFAULT, 253, 824633720832, 824633720832), (DEFAULT, 254, 96, 96), (DEFAULT, 255, 824633720832, 824633720832), (DEFAULT, 256, 96, 96), (DEFAULT, 257, 824633720832, 824633720832), (DEFAULT, 258, 96, 96), (DEFAULT, 259, 824633720832, 824633720832), (DEFAULT, 260, 96, 96), (DEFAULT, 261, 824633720832, 824633720832), (DEFAULT, 262, 96, 96), (DEFAULT, 263, 824633720832, 824633720832), (DEFAULT, 264, 96, 96), (DEFAULT, 265, 824633720832, 824633720832), (DEFAULT, 266, 96, 96), (DEFAULT, 267, 824633720832, 824633720832), (DEFAULT, 268, 96, 96), (DEFAULT, 269, 824633720832, 824633720832), (DEFAULT, 270, 96, 96), (DEFAULT, 271, 824633720832, 824633720832), (DEFAULT, 272, 96, 96), (DEFAULT, 273, 824633720832, 824633720832), (DEFAULT, 274, 96, 96), (DEFAULT, 275, 824633720832, 824633720832), (DEFAULT, 276, 96, 96), (DEFAULT, 277, 824633720832, 824633720832), (DEFAULT, 278, 96, 96), (DEFAULT, 279, 824633720832, 824633720832), (DEFAULT, 280, 96, 96), (DEFAULT, 281, 824633720832, 824633720832), (DEFAULT, 282, 96, 96), (DEFAULT, 283, 824633720832, 824633720832), (DEFAULT, 284, 96, 96), (DEFAULT, 285, 824633720832, 824633720832), (DEFAULT, 286, 96, 96), (DEFAULT, 287, 824633720832, 824633720832), (DEFAULT, 288, 96, 96), (DEFAULT, 289, 824633720832, 824633720832), (DEFAULT, 290, 192, 192), (DEFAULT, 291, 1649267441664, 1649267441664), (DEFAULT, 292, 192, 192), (DEFAULT, 293, 1649267441664, 1649267441664), (DEFAULT, 294, 192, 192), (DEFAULT, 295, 1649267441664, 1649267441664), (DEFAULT, 296, 192, 192), (DEFAULT, 297, 1649267441664, 1649267441664), (DEFAULT, 298, 192, 192), (DEFAULT, 299, 1649267441664, 1649267441664), (DEFAULT, 300, 192, 192), (DEFAULT, 301, 1649267441664, 1649267441664), (DEFAULT, 302, 192, 192), (DEFAULT, 303, 1649267441664, 1649267441664), (DEFAULT, 304, 192, 192), (DEFAULT, 305, 1649267441664, 1649267441664), (DEFAULT, 306, 192, 192), (DEFAULT, 307, 1649267441664, 1649267441664), (DEFAULT, 308, 192, 192), (DEFAULT, 309, 1649267441664, 1649267441664), (DEFAULT, 310, 192, 192), (DEFAULT, 311, 1649267441664, 1649267441664), (DEFAULT, 312, 192, 192), (DEFAULT, 313, 1649267441664, 1649267441664), (DEFAULT, 314, 192, 192), (DEFAULT, 315, 1649267441664, 1649267441664), (DEFAULT, 316, 192, 192), (DEFAULT, 317, 1649267441664, 1649267441664), (DEFAULT, 318, 192, 192), (DEFAULT, 319, 1649267441664, 1649267441664), (DEFAULT, 320, 976, 976), (DEFAULT, 321, 3000000000, 3000000000), (DEFAULT, 322, 976, 976), (DEFAULT, 323, 3000000000, 3000000000), (DEFAULT, 324, 976, 976), (DEFAULT, 325, 3000000000, 3000000000), (DEFAULT, 326, 976, 976), (DEFAULT, 327, 3000000000, 3000000000), (DEFAULT, 328, 976, 976), (DEFAULT, 329, 3000000000, 3000000000), (DEFAULT, 330, 976, 976), (DEFAULT, 331, 3000000000, 3000000000), (DEFAULT, 332, 976, 976), (DEFAULT, 333, 3000000000, 3000000000), (DEFAULT, 334, 976, 976), (DEFAULT, 335, 3000000000, 3000000000), (DEFAULT, 336, 976, 976), (DEFAULT, 337, 3000000000, 3000000000), (DEFAULT, 338, 976, 976), (DEFAULT, 339, 3000000000, 3000000000), (DEFAULT, 340, 976, 976), (DEFAULT, 341, 3000000000, 3000000000), (DEFAULT, 342, 976, 976), (DEFAULT, 343, 3000000000, 3000000000), (DEFAULT, 344, 976, 976), (DEFAULT, 345, 3000000000, 3000000000), (DEFAULT, 346, 976, 976), (DEFAULT, 347, 3000000000, 3000000000), (DEFAULT, 348, 976, 976), (DEFAULT, 349, 3000000000, 3000000000), (DEFAULT, 350, 976, 976), (DEFAULT, 351, 3000000000, 3000000000), (DEFAULT, 352, 976, 976), (DEFAULT, 353, 3000000000, 3000000000), (DEFAULT, 354, 976, 976), (DEFAULT, 355, 3000000000, 3000000000), (DEFAULT, 356, 976, 976), (DEFAULT, 357, 3000000000, 3000000000), (DEFAULT, 358, 976, 976), (DEFAULT, 359, 3000000000, 3000000000), (DEFAULT, 360, 976, 976), (DEFAULT, 361, 3000000000, 3000000000), (DEFAULT, 362, 976, 976), (DEFAULT, 363, 3000000000, 3000000000), (DEFAULT, 364, 976, 976), (DEFAULT, 365, 3000000000, 3000000000), (DEFAULT, 366, 976, 976), (DEFAULT, 367, 3000000000, 3000000000), (DEFAULT, 368, 976, 976), (DEFAULT, 369, 3000000000, 3000000000), (DEFAULT, 370, 976, 976), (DEFAULT, 371, 3000000000, 3000000000), (DEFAULT, 372, 976, 976), (DEFAULT, 373, 3000000000, 3000000000), (DEFAULT, 374, 976, 976), (DEFAULT, 375, 3000000000, 3000000000), (DEFAULT, 376, 976, 976), (DEFAULT, 377, 3000000000, 3000000000), (DEFAULT, 378, 976, 976), (DEFAULT, 379, 3000000000, 3000000000), (DEFAULT, 380, 976, 976), (DEFAULT, 381, 3000000000, 3000000000), (DEFAULT, 382, 976, 976), (DEFAULT, 383, 3000000000, 3000000000), (DEFAULT, 384, 976, 976), (DEFAULT, 385, 3000000000, 3000000000), (DEFAULT, 386, 976, 976), (DEFAULT, 387, 3000000000, 3000000000), (DEFAULT, 388, 976, 976), (DEFAULT, 389, 3000000000, 3000000000), (DEFAULT, 390, 976, 976), (DEFAULT, 391, 3000000000, 3000000000), (DEFAULT, 392, 976, 976), (DEFAULT, 393, 3000000000, 3000000000), (DEFAULT, 394, 976, 976), (DEFAULT, 395, 3000000000, 3000000000), (DEFAULT, 396, 976, 976), (DEFAULT, 397, 3000000000, 3000000000), (DEFAULT, 398, 976, 976), (DEFAULT, 399, 3000000000, 3000000000), (DEFAULT, 400, 976, 976), (DEFAULT, 401, 3000000000, 3000000000), (DEFAULT, 402, 976, 976), (DEFAULT, 403, 3000000000, 3000000000), (DEFAULT, 404, 976, 976), (DEFAULT, 405, 3000000000, 3000000000), (DEFAULT, 406, 976, 976), (DEFAULT, 407, 3000000000, 3000000000), (DEFAULT, 408, 976, 976), (DEFAULT, 409, 3000000000, 3000000000), (DEFAULT, 410, 976, 976), (DEFAULT, 411, 3000000000, 3000000000), (DEFAULT, 412, 976, 976), (DEFAULT, 413, 3000000000, 3000000000), (DEFAULT, 414, 976, 976), (DEFAULT, 415, 3000000000, 3000000000), (DEFAULT, 416, 976, 976), (DEFAULT, 417, 3000000000, 3000000000), (DEFAULT, 418, 976, 976), (DEFAULT, 419, 3000000000, 3000000000), (DEFAULT, 420, 976, 976), (DEFAULT, 421, 3000000000, 3000000000), (DEFAULT, 422, 976, 976), (DEFAULT, 423, 3000000000, 3000000000), (DEFAULT, 424, 976, 976), (DEFAULT, 425, 3000000000, 3000000000), (DEFAULT, 426, 976, 976), (DEFAULT, 427, 3000000000, 3000000000), (DEFAULT, 428, 976, 976), (DEFAULT, 429, 3000000000, 3000000000), (DEFAULT, 430, 976, 976), (DEFAULT, 431, 3000000000, 3000000000), (DEFAULT, 432, 976, 976), (DEFAULT, 433, 3000000000, 3000000000), (DEFAULT, 434, 976, 976), (DEFAULT, 435, 3000000000, 3000000000), (DEFAULT, 436, 976, 976), (DEFAULT, 437, 3000000000, 3000000000), (DEFAULT, 438, 976, 976), (DEFAULT, 439, 3000000000, 3000000000), (DEFAULT, 440, 976, 976), (DEFAULT, 441, 3000000000, 3000000000), (DEFAULT, 442, 976, 976), (DEFAULT, 443, 3000000000, 3000000000), (DEFAULT, 444, 976, 976), (DEFAULT, 445, 3000000000, 3000000000), (DEFAULT, 446, 976, 976), (DEFAULT, 447, 3000000000, 3000000000), (DEFAULT, 448, 976, 976), (DEFAULT, 449, 3000000000, 3000000000), (DEFAULT, 450, 976, 976), (DEFAULT, 451, 3000000000, 3000000000), (DEFAULT, 452, 976, 976), (DEFAULT, 453, 3000000000, 3000000000), (DEFAULT, 454, 976, 976), (DEFAULT, 455, 3000000000, 3000000000), (DEFAULT, 456, 976, 976), (DEFAULT, 457, 3000000000, 3000000000), (DEFAULT, 458, 976, 976), (DEFAULT, 459, 3000000000, 3000000000), (DEFAULT, 460, 976, 976), (DEFAULT, 461, 3000000000, 3000000000), (DEFAULT, 462, 976, 976), (DEFAULT, 463, 3000000000, 3000000000), (DEFAULT, 464, 976, 976), (DEFAULT, 465, 3000000000, 3000000000), (DEFAULT, 466, 976, 976), (DEFAULT, 467, 3000000000, 3000000000), (DEFAULT, 468, 976, 976), (DEFAULT, 469, 3000000000, 3000000000), (DEFAULT, 470, 976, 976), (DEFAULT, 471, 3000000000, 3000000000), (DEFAULT, 472, 976, 976), (DEFAULT, 473, 3000000000, 3000000000), (DEFAULT, 474, 976, 976), (DEFAULT, 475, 3000000000, 3000000000); +INSERT INTO resource_monitoring.resource_capacity VALUES (DEFAULT, 0, 26000000000, 26000000000), (DEFAULT, 1, 24, 24), (DEFAULT, 2, 26000000000, 26000000000), (DEFAULT, 3, 24, 24), (DEFAULT, 4, 26000000000, 26000000000), (DEFAULT, 5, 24, 24), (DEFAULT, 6, 26000000000, 26000000000), (DEFAULT, 7, 24, 24), (DEFAULT, 8, 26000000000, 26000000000), (DEFAULT, 9, 24, 24), (DEFAULT, 10, 26000000000, 26000000000), (DEFAULT, 11, 24, 24), (DEFAULT, 12, 26000000000, 26000000000), (DEFAULT, 13, 24, 24), (DEFAULT, 14, 26000000000, 26000000000), (DEFAULT, 15, 24, 24), (DEFAULT, 16, 26000000000, 26000000000), (DEFAULT, 17, 24, 24), (DEFAULT, 18, 26000000000, 26000000000), (DEFAULT, 19, 24, 24), (DEFAULT, 20, 26000000000, 26000000000), (DEFAULT, 21, 24, 24), (DEFAULT, 22, 26000000000, 26000000000), (DEFAULT, 23, 24, 24), (DEFAULT, 24, 26000000000, 26000000000), (DEFAULT, 25, 24, 24), (DEFAULT, 26, 26000000000, 26000000000), (DEFAULT, 27, 24, 24), (DEFAULT, 28, 26000000000, 26000000000), (DEFAULT, 29, 24, 24), (DEFAULT, 30, 26000000000, 26000000000), (DEFAULT, 31, 24, 24), (DEFAULT, 32, 26000000000, 26000000000), (DEFAULT, 33, 24, 24), (DEFAULT, 34, 26000000000, 26000000000), (DEFAULT, 35, 24, 24), (DEFAULT, 36, 26000000000, 26000000000), (DEFAULT, 37, 24, 24), (DEFAULT, 38, 26000000000, 26000000000), (DEFAULT, 39, 24, 24), (DEFAULT, 40, 26000000000, 26000000000), (DEFAULT, 41, 24, 24), (DEFAULT, 42, 26000000000, 26000000000), (DEFAULT, 43, 24, 24), (DEFAULT, 44, 26000000000, 26000000000), (DEFAULT, 45, 24, 24), (DEFAULT, 46, 26000000000, 26000000000), (DEFAULT, 47, 24, 24), (DEFAULT, 48, 26000000000, 26000000000), (DEFAULT, 49, 24, 24), (DEFAULT, 50, 26000000000, 26000000000), (DEFAULT, 51, 24, 24), (DEFAULT, 52, 26000000000, 26000000000), (DEFAULT, 53, 24, 24), (DEFAULT, 54, 26000000000, 26000000000), (DEFAULT, 55, 24, 24), (DEFAULT, 56, 26000000000, 26000000000), (DEFAULT, 57, 24, 24), (DEFAULT, 58, 26000000000, 26000000000), (DEFAULT, 59, 24, 24), (DEFAULT, 60, 26000000000, 26000000000), (DEFAULT, 61, 24, 24), (DEFAULT, 62, 26000000000, 26000000000), (DEFAULT, 63, 24, 24), (DEFAULT, 64, 26000000000, 26000000000), (DEFAULT, 65, 24, 24), (DEFAULT, 66, 26000000000, 26000000000), (DEFAULT, 67, 24, 24), (DEFAULT, 68, 26000000000, 26000000000), (DEFAULT, 69, 24, 24), (DEFAULT, 70, 26000000000, 26000000000), (DEFAULT, 71, 24, 24), (DEFAULT, 72, 26000000000, 26000000000), (DEFAULT, 73, 24, 24), (DEFAULT, 74, 26000000000, 26000000000), (DEFAULT, 75, 24, 24), (DEFAULT, 76, 26000000000, 26000000000), (DEFAULT, 77, 24, 24), (DEFAULT, 78, 26000000000, 26000000000), (DEFAULT, 79, 24, 24), (DEFAULT, 80, 26000000000, 26000000000), (DEFAULT, 81, 24, 24), (DEFAULT, 82, 26000000000, 26000000000), (DEFAULT, 83, 24, 24), (DEFAULT, 84, 26000000000, 26000000000), (DEFAULT, 85, 24, 24), (DEFAULT, 86, 26000000000, 26000000000), (DEFAULT, 87, 24, 24), (DEFAULT, 88, 26000000000, 26000000000), (DEFAULT, 89, 24, 24), (DEFAULT, 90, 26000000000, 26000000000), (DEFAULT, 91, 24, 24), (DEFAULT, 92, 26000000000, 26000000000), (DEFAULT, 93, 24, 24), (DEFAULT, 94, 26000000000, 26000000000), (DEFAULT, 95, 24, 24), (DEFAULT, 96, 26000000000, 26000000000), (DEFAULT, 97, 24, 24), (DEFAULT, 98, 26000000000, 26000000000), (DEFAULT, 99, 24, 24), (DEFAULT, 100, 52000000000, 52000000000), (DEFAULT, 101, 24, 24), (DEFAULT, 102, 52000000000, 52000000000), (DEFAULT, 103, 24, 24), (DEFAULT, 104, 52000000000, 52000000000), (DEFAULT, 105, 24, 24), (DEFAULT, 106, 52000000000, 52000000000), (DEFAULT, 107, 24, 24), (DEFAULT, 108, 52000000000, 52000000000), (DEFAULT, 109, 24, 24), (DEFAULT, 110, 52000000000, 52000000000), (DEFAULT, 111, 24, 24), (DEFAULT, 112, 52000000000, 52000000000), (DEFAULT, 113, 24, 24), (DEFAULT, 114, 52000000000, 52000000000), (DEFAULT, 115, 24, 24), (DEFAULT, 116, 687194767360, 687194767360), (DEFAULT, 117, 3450434462023680, 3450434462023680), (DEFAULT, 118, 3774873600, 3774873600), (DEFAULT, 119, 23669957984256, 23669957984256), (DEFAULT, 120, 2030043136, 2030043136), (DEFAULT, 121, 2030043136, 2030043136), (DEFAULT, 122, 7913168961536, 7913168961536), (DEFAULT, 123, 7913168961536, 7913168961536), (DEFAULT, 124, 2030043136, 2030043136), (DEFAULT, 125, 2030043136, 2030043136), (DEFAULT, 126, 7913168961536, 7913168961536), (DEFAULT, 127, 7913168961536, 7913168961536), (DEFAULT, 128, 2030043136, 2030043136), (DEFAULT, 129, 2030043136, 2030043136), (DEFAULT, 130, 7913168961536, 7913168961536), (DEFAULT, 131, 7913168961536, 7913168961536), (DEFAULT, 132, 2030043136, 2030043136), (DEFAULT, 133, 2030043136, 2030043136), (DEFAULT, 134, 7913168961536, 7913168961536), (DEFAULT, 135, 7913168961536, 7913168961536), (DEFAULT, 136, 2030043136, 2030043136), (DEFAULT, 137, 2030043136, 2030043136), (DEFAULT, 138, 7913168961536, 7913168961536), (DEFAULT, 139, 7913168961536, 7913168961536), (DEFAULT, 140, 2030043136, 2030043136), (DEFAULT, 141, 2030043136, 2030043136), (DEFAULT, 142, 7913168961536, 7913168961536), (DEFAULT, 143, 7913168961536, 7913168961536), (DEFAULT, 144, 2030043136, 2030043136), (DEFAULT, 145, 2030043136, 2030043136), (DEFAULT, 146, 7913168961536, 7913168961536), (DEFAULT, 147, 7913168961536, 7913168961536), (DEFAULT, 148, 2030043136, 2030043136), (DEFAULT, 149, 2030043136, 2030043136), (DEFAULT, 150, 7913168961536, 7913168961536), (DEFAULT, 151, 7913168961536, 7913168961536), (DEFAULT, 152, 2030043136, 2030043136), (DEFAULT, 153, 2030043136, 2030043136), (DEFAULT, 154, 7913168961536, 7913168961536), (DEFAULT, 155, 7913168961536, 7913168961536), (DEFAULT, 156, 2030043136, 2030043136), (DEFAULT, 157, 2030043136, 2030043136), (DEFAULT, 158, 7913168961536, 7913168961536), (DEFAULT, 159, 7913168961536, 7913168961536), (DEFAULT, 160, 2030043136, 2030043136), (DEFAULT, 161, 2030043136, 2030043136), (DEFAULT, 162, 7913168961536, 7913168961536), (DEFAULT, 163, 7913168961536, 7913168961536), (DEFAULT, 164, 2030043136, 2030043136), (DEFAULT, 165, 2030043136, 2030043136), (DEFAULT, 166, 7913168961536, 7913168961536), (DEFAULT, 167, 7913168961536, 7913168961536), (DEFAULT, 168, 2030043136, 2030043136), (DEFAULT, 169, 2030043136, 2030043136), (DEFAULT, 170, 7913168961536, 7913168961536), (DEFAULT, 171, 7913168961536, 7913168961536), (DEFAULT, 172, 2030043136, 2030043136), (DEFAULT, 173, 2030043136, 2030043136), (DEFAULT, 174, 7913168961536, 7913168961536), (DEFAULT, 175, 7913168961536, 7913168961536), (DEFAULT, 176, 2030043136, 2030043136), (DEFAULT, 177, 2030043136, 2030043136), (DEFAULT, 178, 7913168961536, 7913168961536), (DEFAULT, 179, 7913168961536, 7913168961536), (DEFAULT, 180, 2030043136, 2030043136), (DEFAULT, 181, 2030043136, 2030043136), (DEFAULT, 182, 7913168961536, 7913168961536), (DEFAULT, 183, 7913168961536, 7913168961536), (DEFAULT, 184, 2030043136, 2030043136), (DEFAULT, 185, 2030043136, 2030043136), (DEFAULT, 186, 7913168961536, 7913168961536), (DEFAULT, 187, 7913168961536, 7913168961536), (DEFAULT, 188, 2030043136, 2030043136), (DEFAULT, 189, 2030043136, 2030043136), (DEFAULT, 190, 7913168961536, 7913168961536), (DEFAULT, 191, 7913168961536, 7913168961536), (DEFAULT, 192, 2030043136, 2030043136), (DEFAULT, 193, 2030043136, 2030043136), (DEFAULT, 194, 7913168961536, 7913168961536), (DEFAULT, 195, 7913168961536, 7913168961536), (DEFAULT, 196, 2030043136, 2030043136), (DEFAULT, 197, 2030043136, 2030043136), (DEFAULT, 198, 7913168961536, 7913168961536), (DEFAULT, 199, 7913168961536, 7913168961536), (DEFAULT, 200, 2030043136, 2030043136), (DEFAULT, 201, 2030043136, 2030043136), (DEFAULT, 202, 7913168961536, 7913168961536), (DEFAULT, 203, 7913168961536, 7913168961536), (DEFAULT, 204, 2030043136, 2030043136), (DEFAULT, 205, 2030043136, 2030043136), (DEFAULT, 206, 7913168961536, 7913168961536), (DEFAULT, 207, 7913168961536, 7913168961536), (DEFAULT, 208, 2030043136, 2030043136), (DEFAULT, 209, 2030043136, 2030043136), (DEFAULT, 210, 7913168961536, 7913168961536), (DEFAULT, 211, 7913168961536, 7913168961536), (DEFAULT, 212, 96, 96), (DEFAULT, 213, 824633720832, 824633720832), (DEFAULT, 214, 96, 96), (DEFAULT, 215, 824633720832, 824633720832), (DEFAULT, 216, 96, 96), (DEFAULT, 217, 824633720832, 824633720832), (DEFAULT, 218, 96, 96), (DEFAULT, 219, 824633720832, 824633720832), (DEFAULT, 220, 96, 96), (DEFAULT, 221, 824633720832, 824633720832), (DEFAULT, 222, 96, 96), (DEFAULT, 223, 824633720832, 824633720832), (DEFAULT, 224, 96, 96), (DEFAULT, 225, 824633720832, 824633720832), (DEFAULT, 226, 96, 96), (DEFAULT, 227, 824633720832, 824633720832), (DEFAULT, 228, 96, 96), (DEFAULT, 229, 824633720832, 824633720832), (DEFAULT, 230, 96, 96), (DEFAULT, 231, 824633720832, 824633720832), (DEFAULT, 232, 96, 96), (DEFAULT, 233, 824633720832, 824633720832), (DEFAULT, 234, 96, 96), (DEFAULT, 235, 824633720832, 824633720832), (DEFAULT, 236, 96, 96), (DEFAULT, 237, 824633720832, 824633720832), (DEFAULT, 238, 96, 96), (DEFAULT, 239, 824633720832, 824633720832), (DEFAULT, 240, 96, 96), (DEFAULT, 241, 824633720832, 824633720832), (DEFAULT, 242, 96, 96), (DEFAULT, 243, 824633720832, 824633720832), (DEFAULT, 244, 96, 96), (DEFAULT, 245, 824633720832, 824633720832), (DEFAULT, 246, 96, 96), (DEFAULT, 247, 824633720832, 824633720832), (DEFAULT, 248, 96, 96), (DEFAULT, 249, 824633720832, 824633720832), (DEFAULT, 250, 96, 96), (DEFAULT, 251, 824633720832, 824633720832), (DEFAULT, 252, 96, 96), (DEFAULT, 253, 824633720832, 824633720832), (DEFAULT, 254, 96, 96), (DEFAULT, 255, 824633720832, 824633720832), (DEFAULT, 256, 96, 96), (DEFAULT, 257, 824633720832, 824633720832), (DEFAULT, 258, 96, 96), (DEFAULT, 259, 824633720832, 824633720832), (DEFAULT, 260, 96, 96), (DEFAULT, 261, 824633720832, 824633720832), (DEFAULT, 262, 96, 96), (DEFAULT, 263, 824633720832, 824633720832), (DEFAULT, 264, 96, 96), (DEFAULT, 265, 824633720832, 824633720832), (DEFAULT, 266, 96, 96), (DEFAULT, 267, 824633720832, 824633720832), (DEFAULT, 268, 96, 96), (DEFAULT, 269, 824633720832, 824633720832), (DEFAULT, 270, 96, 96), (DEFAULT, 271, 824633720832, 824633720832), (DEFAULT, 272, 96, 96), (DEFAULT, 273, 824633720832, 824633720832), (DEFAULT, 274, 96, 96), (DEFAULT, 275, 824633720832, 824633720832), (DEFAULT, 276, 96, 96), (DEFAULT, 277, 824633720832, 824633720832), (DEFAULT, 278, 96, 96), (DEFAULT, 279, 824633720832, 824633720832), (DEFAULT, 280, 96, 96), (DEFAULT, 281, 824633720832, 824633720832), (DEFAULT, 282, 96, 96), (DEFAULT, 283, 824633720832, 824633720832), (DEFAULT, 284, 96, 96), (DEFAULT, 285, 824633720832, 824633720832), (DEFAULT, 286, 96, 96), (DEFAULT, 287, 824633720832, 824633720832), (DEFAULT, 288, 96, 96), (DEFAULT, 289, 824633720832, 824633720832), (DEFAULT, 290, 192, 192), (DEFAULT, 291, 1649267441664, 1649267441664), (DEFAULT, 292, 192, 192), (DEFAULT, 293, 1649267441664, 1649267441664), (DEFAULT, 294, 192, 192), (DEFAULT, 295, 1649267441664, 1649267441664), (DEFAULT, 296, 192, 192), (DEFAULT, 297, 1649267441664, 1649267441664), (DEFAULT, 298, 192, 192), (DEFAULT, 299, 1649267441664, 1649267441664), (DEFAULT, 300, 192, 192), (DEFAULT, 301, 1649267441664, 1649267441664), (DEFAULT, 302, 192, 192), (DEFAULT, 303, 1649267441664, 1649267441664), (DEFAULT, 304, 192, 192), (DEFAULT, 305, 1649267441664, 1649267441664), (DEFAULT, 306, 192, 192), (DEFAULT, 307, 1649267441664, 1649267441664), (DEFAULT, 308, 192, 192), (DEFAULT, 309, 1649267441664, 1649267441664), (DEFAULT, 310, 192, 192), (DEFAULT, 311, 1649267441664, 1649267441664), (DEFAULT, 312, 192, 192), (DEFAULT, 313, 1649267441664, 1649267441664), (DEFAULT, 314, 192, 192), (DEFAULT, 315, 1649267441664, 1649267441664), (DEFAULT, 316, 192, 192), (DEFAULT, 317, 1649267441664, 1649267441664), (DEFAULT, 318, 192, 192), (DEFAULT, 319, 1649267441664, 1649267441664), (DEFAULT, 320, 3904, 3904), (DEFAULT, 321, 3000000000, 3000000000), (DEFAULT, 322, 3904, 3904), (DEFAULT, 323, 3000000000, 3000000000), (DEFAULT, 324, 3904, 3904), (DEFAULT, 325, 3000000000, 3000000000), (DEFAULT, 326, 3904, 3904), (DEFAULT, 327, 3000000000, 3000000000), (DEFAULT, 328, 3904, 3904), (DEFAULT, 329, 3000000000, 3000000000), (DEFAULT, 330, 3904, 3904), (DEFAULT, 331, 3000000000, 3000000000), (DEFAULT, 332, 3904, 3904), (DEFAULT, 333, 3000000000, 3000000000), (DEFAULT, 334, 3904, 3904), (DEFAULT, 335, 3000000000, 3000000000), (DEFAULT, 336, 3904, 3904), (DEFAULT, 337, 3000000000, 3000000000), (DEFAULT, 338, 3904, 3904), (DEFAULT, 339, 3000000000, 3000000000), (DEFAULT, 340, 3904, 3904), (DEFAULT, 341, 3000000000, 3000000000), (DEFAULT, 342, 3904, 3904), (DEFAULT, 343, 3000000000, 3000000000), (DEFAULT, 344, 3904, 3904), (DEFAULT, 345, 3000000000, 3000000000), (DEFAULT, 346, 3904, 3904), (DEFAULT, 347, 3000000000, 3000000000), (DEFAULT, 348, 3904, 3904), (DEFAULT, 349, 3000000000, 3000000000), (DEFAULT, 350, 3904, 3904), (DEFAULT, 351, 3000000000, 3000000000), (DEFAULT, 352, 3904, 3904), (DEFAULT, 353, 3000000000, 3000000000), (DEFAULT, 354, 3904, 3904), (DEFAULT, 355, 3000000000, 3000000000), (DEFAULT, 356, 3904, 3904), (DEFAULT, 357, 3000000000, 3000000000), (DEFAULT, 358, 3904, 3904), (DEFAULT, 359, 3000000000, 3000000000), (DEFAULT, 360, 3904, 3904), (DEFAULT, 361, 3000000000, 3000000000), (DEFAULT, 362, 3904, 3904), (DEFAULT, 363, 3000000000, 3000000000), (DEFAULT, 364, 3904, 3904), (DEFAULT, 365, 3000000000, 3000000000), (DEFAULT, 366, 3904, 3904), (DEFAULT, 367, 3000000000, 3000000000), (DEFAULT, 368, 3904, 3904), (DEFAULT, 369, 3000000000, 3000000000), (DEFAULT, 370, 3904, 3904), (DEFAULT, 371, 3000000000, 3000000000), (DEFAULT, 372, 3904, 3904), (DEFAULT, 373, 3000000000, 3000000000), (DEFAULT, 374, 3904, 3904), (DEFAULT, 375, 3000000000, 3000000000), (DEFAULT, 376, 3904, 3904), (DEFAULT, 377, 3000000000, 3000000000), (DEFAULT, 378, 3904, 3904), (DEFAULT, 379, 3000000000, 3000000000), (DEFAULT, 380, 3904, 3904), (DEFAULT, 381, 3000000000, 3000000000), (DEFAULT, 382, 3904, 3904), (DEFAULT, 383, 3000000000, 3000000000), (DEFAULT, 384, 3904, 3904), (DEFAULT, 385, 3000000000, 3000000000), (DEFAULT, 386, 3904, 3904), (DEFAULT, 387, 3000000000, 3000000000), (DEFAULT, 388, 3904, 3904), (DEFAULT, 389, 3000000000, 3000000000), (DEFAULT, 390, 3904, 3904), (DEFAULT, 391, 3000000000, 3000000000), (DEFAULT, 392, 3904, 3904), (DEFAULT, 393, 3000000000, 3000000000), (DEFAULT, 394, 3904, 3904), (DEFAULT, 395, 3000000000, 3000000000), (DEFAULT, 396, 3904, 3904), (DEFAULT, 397, 3000000000, 3000000000), (DEFAULT, 398, 3904, 3904), (DEFAULT, 399, 3000000000, 3000000000), (DEFAULT, 400, 3904, 3904), (DEFAULT, 401, 3000000000, 3000000000), (DEFAULT, 402, 3904, 3904), (DEFAULT, 403, 3000000000, 3000000000), (DEFAULT, 404, 3904, 3904), (DEFAULT, 405, 3000000000, 3000000000), (DEFAULT, 406, 3904, 3904), (DEFAULT, 407, 3000000000, 3000000000), (DEFAULT, 408, 3904, 3904), (DEFAULT, 409, 3000000000, 3000000000), (DEFAULT, 410, 3904, 3904), (DEFAULT, 411, 3000000000, 3000000000), (DEFAULT, 412, 3904, 3904), (DEFAULT, 413, 3000000000, 3000000000), (DEFAULT, 414, 3904, 3904), (DEFAULT, 415, 3000000000, 3000000000), (DEFAULT, 416, 3904, 3904), (DEFAULT, 417, 3000000000, 3000000000), (DEFAULT, 418, 3904, 3904), (DEFAULT, 419, 3000000000, 3000000000), (DEFAULT, 420, 3904, 3904), (DEFAULT, 421, 3000000000, 3000000000), (DEFAULT, 422, 3904, 3904), (DEFAULT, 423, 3000000000, 3000000000), (DEFAULT, 424, 3904, 3904), (DEFAULT, 425, 3000000000, 3000000000), (DEFAULT, 426, 3904, 3904), (DEFAULT, 427, 3000000000, 3000000000), (DEFAULT, 428, 3904, 3904), (DEFAULT, 429, 3000000000, 3000000000), (DEFAULT, 430, 3904, 3904), (DEFAULT, 431, 3000000000, 3000000000), (DEFAULT, 432, 3904, 3904), (DEFAULT, 433, 3000000000, 3000000000), (DEFAULT, 434, 3904, 3904), (DEFAULT, 435, 3000000000, 3000000000), (DEFAULT, 436, 3904, 3904), (DEFAULT, 437, 3000000000, 3000000000), (DEFAULT, 438, 3904, 3904), (DEFAULT, 439, 3000000000, 3000000000), (DEFAULT, 440, 3904, 3904), (DEFAULT, 441, 3000000000, 3000000000), (DEFAULT, 442, 3904, 3904), (DEFAULT, 443, 3000000000, 3000000000), (DEFAULT, 444, 3904, 3904), (DEFAULT, 445, 3000000000, 3000000000), (DEFAULT, 446, 3904, 3904), (DEFAULT, 447, 3000000000, 3000000000), (DEFAULT, 448, 3904, 3904), (DEFAULT, 449, 3000000000, 3000000000), (DEFAULT, 450, 3904, 3904), (DEFAULT, 451, 3000000000, 3000000000), (DEFAULT, 452, 3904, 3904), (DEFAULT, 453, 3000000000, 3000000000), (DEFAULT, 454, 3904, 3904), (DEFAULT, 455, 3000000000, 3000000000), (DEFAULT, 456, 3904, 3904), (DEFAULT, 457, 3000000000, 3000000000), (DEFAULT, 458, 3904, 3904), (DEFAULT, 459, 3000000000, 3000000000), (DEFAULT, 460, 3904, 3904), (DEFAULT, 461, 3000000000, 3000000000), (DEFAULT, 462, 3904, 3904), (DEFAULT, 463, 3000000000, 3000000000), (DEFAULT, 464, 3904, 3904), (DEFAULT, 465, 3000000000, 3000000000), (DEFAULT, 466, 3904, 3904), (DEFAULT, 467, 3000000000, 3000000000), (DEFAULT, 468, 3904, 3904), (DEFAULT, 469, 3000000000, 3000000000), (DEFAULT, 470, 3904, 3904), (DEFAULT, 471, 3000000000, 3000000000), (DEFAULT, 472, 3904, 3904), (DEFAULT, 473, 3000000000, 3000000000), (DEFAULT, 474, 3904, 3904), (DEFAULT, 475, 3000000000, 3000000000); INSERT INTO resource_monitoring.resource_availability VALUES (DEFAULT, 0, TRUE), (DEFAULT, 1, TRUE), (DEFAULT, 2, TRUE), (DEFAULT, 3, TRUE), (DEFAULT, 4, TRUE), (DEFAULT, 5, TRUE), (DEFAULT, 6, TRUE), (DEFAULT, 7, TRUE), (DEFAULT, 8, TRUE), (DEFAULT, 9, TRUE), (DEFAULT, 10, TRUE), (DEFAULT, 11, TRUE), (DEFAULT, 12, TRUE), (DEFAULT, 13, TRUE), (DEFAULT, 14, TRUE), (DEFAULT, 15, TRUE), (DEFAULT, 16, TRUE), (DEFAULT, 17, TRUE), (DEFAULT, 18, TRUE), (DEFAULT, 19, TRUE), (DEFAULT, 20, TRUE), (DEFAULT, 21, TRUE), (DEFAULT, 22, TRUE), (DEFAULT, 23, TRUE), (DEFAULT, 24, TRUE), (DEFAULT, 25, TRUE), (DEFAULT, 26, TRUE), (DEFAULT, 27, TRUE), (DEFAULT, 28, TRUE), (DEFAULT, 29, TRUE), (DEFAULT, 30, TRUE), (DEFAULT, 31, TRUE), (DEFAULT, 32, TRUE), (DEFAULT, 33, TRUE), (DEFAULT, 34, TRUE), (DEFAULT, 35, TRUE), (DEFAULT, 36, TRUE), (DEFAULT, 37, TRUE), (DEFAULT, 38, TRUE), (DEFAULT, 39, TRUE), (DEFAULT, 40, TRUE), (DEFAULT, 41, TRUE), (DEFAULT, 42, TRUE), (DEFAULT, 43, TRUE), (DEFAULT, 44, TRUE), (DEFAULT, 45, TRUE), (DEFAULT, 46, TRUE), (DEFAULT, 47, TRUE), (DEFAULT, 48, TRUE), (DEFAULT, 49, TRUE), (DEFAULT, 50, TRUE), (DEFAULT, 51, TRUE), (DEFAULT, 52, TRUE), (DEFAULT, 53, TRUE), (DEFAULT, 54, TRUE), (DEFAULT, 55, TRUE), (DEFAULT, 56, TRUE), (DEFAULT, 57, TRUE), (DEFAULT, 58, TRUE), (DEFAULT, 59, TRUE), (DEFAULT, 60, TRUE), (DEFAULT, 61, TRUE), (DEFAULT, 62, TRUE), (DEFAULT, 63, TRUE), (DEFAULT, 64, TRUE), (DEFAULT, 65, TRUE), (DEFAULT, 66, TRUE), (DEFAULT, 67, TRUE), (DEFAULT, 68, TRUE), (DEFAULT, 69, TRUE), (DEFAULT, 70, TRUE), (DEFAULT, 71, TRUE), (DEFAULT, 72, TRUE), (DEFAULT, 73, TRUE), (DEFAULT, 74, TRUE), (DEFAULT, 75, TRUE), (DEFAULT, 76, TRUE), (DEFAULT, 77, TRUE), (DEFAULT, 78, TRUE), (DEFAULT, 79, TRUE), (DEFAULT, 80, TRUE), (DEFAULT, 81, TRUE), (DEFAULT, 82, TRUE), (DEFAULT, 83, TRUE), (DEFAULT, 84, TRUE), (DEFAULT, 85, TRUE), (DEFAULT, 86, TRUE), (DEFAULT, 87, TRUE), (DEFAULT, 88, TRUE), (DEFAULT, 89, TRUE), (DEFAULT, 90, TRUE), (DEFAULT, 91, TRUE), (DEFAULT, 92, TRUE), (DEFAULT, 93, TRUE), (DEFAULT, 94, TRUE), (DEFAULT, 95, TRUE), (DEFAULT, 96, TRUE), (DEFAULT, 97, TRUE), (DEFAULT, 98, TRUE), (DEFAULT, 99, TRUE), (DEFAULT, 100, TRUE), (DEFAULT, 101, TRUE), (DEFAULT, 102, TRUE), (DEFAULT, 103, TRUE), (DEFAULT, 104, TRUE), (DEFAULT, 105, TRUE), (DEFAULT, 106, TRUE), (DEFAULT, 107, TRUE), (DEFAULT, 108, TRUE), (DEFAULT, 109, TRUE), (DEFAULT, 110, TRUE), (DEFAULT, 111, TRUE), (DEFAULT, 112, TRUE), (DEFAULT, 113, TRUE), (DEFAULT, 114, TRUE), (DEFAULT, 115, TRUE), (DEFAULT, 116, TRUE), (DEFAULT, 117, TRUE), (DEFAULT, 118, TRUE), (DEFAULT, 119, FALSE), (DEFAULT, 120, TRUE), (DEFAULT, 121, TRUE), (DEFAULT, 122, TRUE), (DEFAULT, 123, TRUE), (DEFAULT, 124, TRUE), (DEFAULT, 125, TRUE), (DEFAULT, 126, TRUE), (DEFAULT, 127, TRUE), (DEFAULT, 128, TRUE), (DEFAULT, 129, TRUE), (DEFAULT, 130, TRUE), (DEFAULT, 131, TRUE), (DEFAULT, 132, TRUE), (DEFAULT, 133, TRUE), (DEFAULT, 134, TRUE), (DEFAULT, 135, TRUE), (DEFAULT, 136, TRUE), (DEFAULT, 137, TRUE), (DEFAULT, 138, TRUE), (DEFAULT, 139, TRUE), (DEFAULT, 140, TRUE), (DEFAULT, 141, TRUE), (DEFAULT, 142, TRUE), (DEFAULT, 143, TRUE), (DEFAULT, 144, TRUE), (DEFAULT, 145, TRUE), (DEFAULT, 146, TRUE), (DEFAULT, 147, TRUE), (DEFAULT, 148, TRUE), (DEFAULT, 149, TRUE), (DEFAULT, 150, TRUE), (DEFAULT, 151, TRUE), (DEFAULT, 152, TRUE), (DEFAULT, 153, TRUE), (DEFAULT, 154, TRUE), (DEFAULT, 155, TRUE), (DEFAULT, 156, TRUE), (DEFAULT, 157, TRUE), (DEFAULT, 158, TRUE), (DEFAULT, 159, TRUE), (DEFAULT, 160, TRUE), (DEFAULT, 161, TRUE), (DEFAULT, 162, TRUE), (DEFAULT, 163, TRUE), (DEFAULT, 164, TRUE), (DEFAULT, 165, TRUE), (DEFAULT, 166, TRUE), (DEFAULT, 167, TRUE), (DEFAULT, 168, TRUE), (DEFAULT, 169, TRUE), (DEFAULT, 170, TRUE), (DEFAULT, 171, TRUE), (DEFAULT, 172, TRUE), (DEFAULT, 173, TRUE), (DEFAULT, 174, TRUE), (DEFAULT, 175, TRUE), (DEFAULT, 176, TRUE), (DEFAULT, 177, TRUE), (DEFAULT, 178, TRUE), (DEFAULT, 179, TRUE), (DEFAULT, 180, TRUE), (DEFAULT, 181, TRUE), (DEFAULT, 182, TRUE), (DEFAULT, 183, TRUE), (DEFAULT, 184, TRUE), (DEFAULT, 185, TRUE), (DEFAULT, 186, TRUE), (DEFAULT, 187, TRUE), (DEFAULT, 188, TRUE), (DEFAULT, 189, TRUE), (DEFAULT, 190, TRUE), (DEFAULT, 191, TRUE), (DEFAULT, 192, TRUE), (DEFAULT, 193, TRUE), (DEFAULT, 194, TRUE), (DEFAULT, 195, TRUE), (DEFAULT, 196, TRUE), (DEFAULT, 197, TRUE), (DEFAULT, 198, TRUE), (DEFAULT, 199, TRUE), (DEFAULT, 200, TRUE), (DEFAULT, 201, TRUE), (DEFAULT, 202, FALSE), (DEFAULT, 203, FALSE), (DEFAULT, 204, TRUE), (DEFAULT, 205, TRUE), (DEFAULT, 206, FALSE), (DEFAULT, 207, FALSE), (DEFAULT, 208, TRUE), (DEFAULT, 209, TRUE), (DEFAULT, 210, FALSE), (DEFAULT, 211, FALSE), (DEFAULT, 212, TRUE), (DEFAULT, 213, TRUE), (DEFAULT, 214, TRUE), (DEFAULT, 215, TRUE), (DEFAULT, 216, TRUE), (DEFAULT, 217, TRUE), (DEFAULT, 218, TRUE), (DEFAULT, 219, TRUE), (DEFAULT, 220, TRUE), (DEFAULT, 221, TRUE), (DEFAULT, 222, TRUE), (DEFAULT, 223, TRUE), (DEFAULT, 224, TRUE), (DEFAULT, 225, TRUE), (DEFAULT, 226, TRUE), (DEFAULT, 227, TRUE), (DEFAULT, 228, TRUE), (DEFAULT, 229, TRUE), (DEFAULT, 230, TRUE), (DEFAULT, 231, TRUE), (DEFAULT, 232, TRUE), (DEFAULT, 233, TRUE), (DEFAULT, 234, TRUE), (DEFAULT, 235, TRUE), (DEFAULT, 236, TRUE), (DEFAULT, 237, TRUE), (DEFAULT, 238, TRUE), (DEFAULT, 239, TRUE), (DEFAULT, 240, TRUE), (DEFAULT, 241, TRUE), (DEFAULT, 242, TRUE), (DEFAULT, 243, TRUE), (DEFAULT, 244, TRUE), (DEFAULT, 245, TRUE), (DEFAULT, 246, TRUE), (DEFAULT, 247, TRUE), (DEFAULT, 248, TRUE), (DEFAULT, 249, TRUE), (DEFAULT, 250, TRUE), (DEFAULT, 251, TRUE), (DEFAULT, 252, TRUE), (DEFAULT, 253, TRUE), (DEFAULT, 254, TRUE), (DEFAULT, 255, TRUE), (DEFAULT, 256, TRUE), (DEFAULT, 257, TRUE), (DEFAULT, 258, TRUE), (DEFAULT, 259, TRUE), (DEFAULT, 260, TRUE), (DEFAULT, 261, TRUE), (DEFAULT, 262, TRUE), (DEFAULT, 263, TRUE), (DEFAULT, 264, TRUE), (DEFAULT, 265, TRUE), (DEFAULT, 266, TRUE), (DEFAULT, 267, TRUE), (DEFAULT, 268, TRUE), (DEFAULT, 269, TRUE), (DEFAULT, 270, TRUE), (DEFAULT, 271, TRUE), (DEFAULT, 272, TRUE), (DEFAULT, 273, TRUE), (DEFAULT, 274, TRUE), (DEFAULT, 275, TRUE), (DEFAULT, 276, TRUE), (DEFAULT, 277, TRUE), (DEFAULT, 278, TRUE), (DEFAULT, 279, TRUE), (DEFAULT, 280, TRUE), (DEFAULT, 281, TRUE), (DEFAULT, 282, TRUE), (DEFAULT, 283, TRUE), (DEFAULT, 284, TRUE), (DEFAULT, 285, TRUE), (DEFAULT, 286, TRUE), (DEFAULT, 287, TRUE), (DEFAULT, 288, TRUE), (DEFAULT, 289, TRUE), (DEFAULT, 290, TRUE), (DEFAULT, 291, TRUE), (DEFAULT, 292, TRUE), (DEFAULT, 293, TRUE), (DEFAULT, 294, TRUE), (DEFAULT, 295, TRUE), (DEFAULT, 296, TRUE), (DEFAULT, 297, TRUE), (DEFAULT, 298, TRUE), (DEFAULT, 299, TRUE), (DEFAULT, 300, TRUE), (DEFAULT, 301, TRUE), (DEFAULT, 302, TRUE), (DEFAULT, 303, TRUE), (DEFAULT, 304, TRUE), (DEFAULT, 305, TRUE), (DEFAULT, 306, TRUE), (DEFAULT, 307, TRUE), (DEFAULT, 308, TRUE), (DEFAULT, 309, TRUE), (DEFAULT, 310, TRUE), (DEFAULT, 311, TRUE), (DEFAULT, 312, TRUE), (DEFAULT, 313, TRUE), (DEFAULT, 314, TRUE), (DEFAULT, 315, TRUE), (DEFAULT, 316, TRUE), (DEFAULT, 317, TRUE), (DEFAULT, 318, TRUE), (DEFAULT, 319, TRUE), (DEFAULT, 320, TRUE), (DEFAULT, 321, TRUE), (DEFAULT, 322, TRUE), (DEFAULT, 323, TRUE), (DEFAULT, 324, TRUE), (DEFAULT, 325, TRUE), (DEFAULT, 326, TRUE), (DEFAULT, 327, TRUE), (DEFAULT, 328, TRUE), (DEFAULT, 329, TRUE), (DEFAULT, 330, TRUE), (DEFAULT, 331, TRUE), (DEFAULT, 332, TRUE), (DEFAULT, 333, TRUE), (DEFAULT, 334, TRUE), (DEFAULT, 335, TRUE), (DEFAULT, 336, TRUE), (DEFAULT, 337, TRUE), (DEFAULT, 338, TRUE), (DEFAULT, 339, TRUE), (DEFAULT, 340, TRUE), (DEFAULT, 341, TRUE), (DEFAULT, 342, TRUE), (DEFAULT, 343, TRUE), (DEFAULT, 344, TRUE), (DEFAULT, 345, TRUE), (DEFAULT, 346, TRUE), (DEFAULT, 347, TRUE), (DEFAULT, 348, TRUE), (DEFAULT, 349, TRUE), (DEFAULT, 350, TRUE), (DEFAULT, 351, TRUE), (DEFAULT, 352, TRUE), (DEFAULT, 353, TRUE), (DEFAULT, 354, TRUE), (DEFAULT, 355, TRUE), (DEFAULT, 356, TRUE), (DEFAULT, 357, TRUE), (DEFAULT, 358, TRUE), (DEFAULT, 359, TRUE), (DEFAULT, 360, TRUE), (DEFAULT, 361, TRUE), (DEFAULT, 362, TRUE), (DEFAULT, 363, TRUE), (DEFAULT, 364, TRUE), (DEFAULT, 365, TRUE), (DEFAULT, 366, TRUE), (DEFAULT, 367, TRUE), (DEFAULT, 368, TRUE), (DEFAULT, 369, TRUE), (DEFAULT, 370, TRUE), (DEFAULT, 371, TRUE), (DEFAULT, 372, TRUE), (DEFAULT, 373, TRUE), (DEFAULT, 374, TRUE), (DEFAULT, 375, TRUE), (DEFAULT, 376, TRUE), (DEFAULT, 377, TRUE), (DEFAULT, 378, TRUE), (DEFAULT, 379, TRUE), (DEFAULT, 380, TRUE), (DEFAULT, 381, TRUE), (DEFAULT, 382, TRUE), (DEFAULT, 383, TRUE), (DEFAULT, 384, TRUE), (DEFAULT, 385, TRUE), (DEFAULT, 386, TRUE), (DEFAULT, 387, TRUE), (DEFAULT, 388, TRUE), (DEFAULT, 389, TRUE), (DEFAULT, 390, TRUE), (DEFAULT, 391, TRUE), (DEFAULT, 392, TRUE), (DEFAULT, 393, TRUE), (DEFAULT, 394, TRUE), (DEFAULT, 395, TRUE), (DEFAULT, 396, TRUE), (DEFAULT, 397, TRUE), (DEFAULT, 398, TRUE), (DEFAULT, 399, TRUE), (DEFAULT, 400, TRUE), (DEFAULT, 401, TRUE), (DEFAULT, 402, TRUE), (DEFAULT, 403, TRUE), (DEFAULT, 404, TRUE), (DEFAULT, 405, TRUE), (DEFAULT, 406, TRUE), (DEFAULT, 407, TRUE), (DEFAULT, 408, TRUE), (DEFAULT, 409, TRUE), (DEFAULT, 410, TRUE), (DEFAULT, 411, TRUE), (DEFAULT, 412, TRUE), (DEFAULT, 413, TRUE), (DEFAULT, 414, TRUE), (DEFAULT, 415, TRUE), (DEFAULT, 416, TRUE), (DEFAULT, 417, TRUE), (DEFAULT, 418, TRUE), (DEFAULT, 419, TRUE), (DEFAULT, 420, TRUE), (DEFAULT, 421, TRUE), (DEFAULT, 422, TRUE), (DEFAULT, 423, TRUE), (DEFAULT, 424, TRUE), (DEFAULT, 425, TRUE), (DEFAULT, 426, TRUE), (DEFAULT, 427, TRUE), (DEFAULT, 428, TRUE), (DEFAULT, 429, TRUE), (DEFAULT, 430, TRUE), (DEFAULT, 431, TRUE), (DEFAULT, 432, TRUE), (DEFAULT, 433, TRUE), (DEFAULT, 434, TRUE), (DEFAULT, 435, TRUE), (DEFAULT, 436, TRUE), (DEFAULT, 437, TRUE), (DEFAULT, 438, TRUE), (DEFAULT, 439, TRUE), (DEFAULT, 440, TRUE), (DEFAULT, 441, TRUE), (DEFAULT, 442, TRUE), (DEFAULT, 443, TRUE), (DEFAULT, 444, TRUE), (DEFAULT, 445, TRUE), (DEFAULT, 446, TRUE), (DEFAULT, 447, TRUE), (DEFAULT, 448, TRUE), (DEFAULT, 449, TRUE), (DEFAULT, 450, TRUE), (DEFAULT, 451, TRUE), (DEFAULT, 452, TRUE), (DEFAULT, 453, TRUE), (DEFAULT, 454, TRUE), (DEFAULT, 455, TRUE), (DEFAULT, 456, TRUE), (DEFAULT, 457, TRUE), (DEFAULT, 458, TRUE), (DEFAULT, 459, TRUE), (DEFAULT, 460, TRUE), (DEFAULT, 461, TRUE), (DEFAULT, 462, TRUE), (DEFAULT, 463, TRUE), (DEFAULT, 464, TRUE), (DEFAULT, 465, TRUE), (DEFAULT, 466, TRUE), (DEFAULT, 467, TRUE), (DEFAULT, 468, TRUE), (DEFAULT, 469, TRUE), (DEFAULT, 470, TRUE), (DEFAULT, 471, TRUE), (DEFAULT, 472, TRUE), (DEFAULT, 473, TRUE), (DEFAULT, 474, TRUE), (DEFAULT, 475, TRUE); -INSERT INTO virtual_instrument.resource_group_to_resource_group VALUES (DEFAULT, 156, 135), (DEFAULT, 157, 135), (DEFAULT, 158, 135), (DEFAULT, 159, 135), (DEFAULT, 160, 135), (DEFAULT, 161, 135), (DEFAULT, 162, 135), (DEFAULT, 163, 135), (DEFAULT, 164, 135), (DEFAULT, 165, 135), (DEFAULT, 166, 135), (DEFAULT, 167, 135), (DEFAULT, 168, 135), (DEFAULT, 169, 135), (DEFAULT, 170, 135), (DEFAULT, 171, 135), (DEFAULT, 172, 135), (DEFAULT, 173, 136), (DEFAULT, 174, 136), (DEFAULT, 175, 136), (DEFAULT, 176, 136), (DEFAULT, 177, 136), (DEFAULT, 178, 136), (DEFAULT, 179, 136), (DEFAULT, 180, 136), (DEFAULT, 181, 136), (DEFAULT, 182, 136), (DEFAULT, 183, 136), (DEFAULT, 184, 136), (DEFAULT, 185, 136), (DEFAULT, 186, 136), (DEFAULT, 187, 136), (DEFAULT, 188, 137), (DEFAULT, 189, 137), (DEFAULT, 190, 137), (DEFAULT, 191, 137), (DEFAULT, 192, 137), (DEFAULT, 193, 137), (DEFAULT, 194, 137), (DEFAULT, 195, 137), (DEFAULT, 196, 137), (DEFAULT, 197, 137), (DEFAULT, 198, 137), (DEFAULT, 199, 137), (DEFAULT, 200, 137), (DEFAULT, 201, 137), (DEFAULT, 202, 137), (DEFAULT, 206, 149), (DEFAULT, 207, 149), (DEFAULT, 208, 150), (DEFAULT, 209, 150), (DEFAULT, 210, 151), (DEFAULT, 211, 151), (DEFAULT, 212, 152), (DEFAULT, 213, 152), (DEFAULT, 214, 153), (DEFAULT, 215, 153), (DEFAULT, 216, 154), (DEFAULT, 217, 154), (DEFAULT, 218, 155), (DEFAULT, 219, 155), (DEFAULT, 220, 156), (DEFAULT, 221, 156), (DEFAULT, 222, 157), (DEFAULT, 223, 157), (DEFAULT, 224, 158), (DEFAULT, 225, 158), (DEFAULT, 226, 159), (DEFAULT, 227, 159), (DEFAULT, 228, 160), (DEFAULT, 229, 160), (DEFAULT, 230, 161), (DEFAULT, 231, 161), (DEFAULT, 232, 162), (DEFAULT, 233, 162), (DEFAULT, 234, 163), (DEFAULT, 235, 163), (DEFAULT, 236, 164), (DEFAULT, 237, 164), (DEFAULT, 238, 165), (DEFAULT, 239, 165), (DEFAULT, 240, 166), (DEFAULT, 241, 166), (DEFAULT, 242, 167), (DEFAULT, 243, 167), (DEFAULT, 244, 168), (DEFAULT, 245, 168), (DEFAULT, 246, 169), (DEFAULT, 247, 169), (DEFAULT, 248, 170), (DEFAULT, 249, 170), (DEFAULT, 250, 171), (DEFAULT, 251, 171), (DEFAULT, 252, 172), (DEFAULT, 253, 172), (DEFAULT, 254, 173), (DEFAULT, 255, 174), (DEFAULT, 256, 175), (DEFAULT, 257, 176), (DEFAULT, 258, 177), (DEFAULT, 259, 178), (DEFAULT, 260, 179), (DEFAULT, 261, 180), (DEFAULT, 262, 181), (DEFAULT, 263, 182), (DEFAULT, 264, 183), (DEFAULT, 265, 184), (DEFAULT, 266, 185), (DEFAULT, 267, 186), (DEFAULT, 268, 187), (DEFAULT, 269, 188), (DEFAULT, 270, 189), (DEFAULT, 271, 190), (DEFAULT, 272, 191), (DEFAULT, 273, 192), (DEFAULT, 274, 193), (DEFAULT, 275, 194), (DEFAULT, 276, 195), (DEFAULT, 277, 196), (DEFAULT, 278, 197), (DEFAULT, 279, 198), (DEFAULT, 280, 199), (DEFAULT, 281, 200), (DEFAULT, 282, 201), (DEFAULT, 283, 202), (DEFAULT, 149, 138), (DEFAULT, 150, 138), (DEFAULT, 151, 138), (DEFAULT, 152, 138), (DEFAULT, 153, 138), (DEFAULT, 154, 138), (DEFAULT, 155, 141), (DEFAULT, 156, 141), (DEFAULT, 157, 141), (DEFAULT, 158, 141), (DEFAULT, 159, 141), (DEFAULT, 160, 141), (DEFAULT, 161, 141), (DEFAULT, 162, 141), (DEFAULT, 163, 141), (DEFAULT, 164, 141), (DEFAULT, 165, 141), (DEFAULT, 166, 141), (DEFAULT, 167, 141), (DEFAULT, 168, 141), (DEFAULT, 169, 141), (DEFAULT, 170, 141), (DEFAULT, 171, 141), (DEFAULT, 172, 141), (DEFAULT, 149, 143), (DEFAULT, 150, 143), (DEFAULT, 151, 143), (DEFAULT, 152, 143), (DEFAULT, 153, 143), (DEFAULT, 154, 143), (DEFAULT, 156, 143), (DEFAULT, 157, 143), (DEFAULT, 158, 143), (DEFAULT, 159, 143), (DEFAULT, 165, 143), (DEFAULT, 149, 144), (DEFAULT, 150, 144), (DEFAULT, 151, 144), (DEFAULT, 152, 144), (DEFAULT, 153, 144), (DEFAULT, 154, 144), (DEFAULT, 155, 144), (DEFAULT, 156, 144), (DEFAULT, 157, 144), (DEFAULT, 158, 144), (DEFAULT, 159, 144), (DEFAULT, 160, 144), (DEFAULT, 161, 144), (DEFAULT, 162, 144), (DEFAULT, 163, 144), (DEFAULT, 164, 144), (DEFAULT, 165, 144), (DEFAULT, 166, 144), (DEFAULT, 168, 144), (DEFAULT, 169, 144), (DEFAULT, 171, 144), (DEFAULT, 172, 144), (DEFAULT, 172, 139), (DEFAULT, 149, 140), (DEFAULT, 149, 139), (DEFAULT, 150, 140), (DEFAULT, 150, 139), (DEFAULT, 151, 140), (DEFAULT, 151, 139), (DEFAULT, 152, 140), (DEFAULT, 152, 139), (DEFAULT, 153, 140), (DEFAULT, 153, 139), (DEFAULT, 154, 140), (DEFAULT, 154, 139), (DEFAULT, 155, 140), (DEFAULT, 155, 139), (DEFAULT, 156, 140), (DEFAULT, 156, 139), (DEFAULT, 157, 140), (DEFAULT, 157, 139), (DEFAULT, 158, 140), (DEFAULT, 158, 139), (DEFAULT, 159, 140), (DEFAULT, 159, 139), (DEFAULT, 160, 140), (DEFAULT, 160, 139), (DEFAULT, 161, 140), (DEFAULT, 161, 139), (DEFAULT, 162, 140), (DEFAULT, 162, 139), (DEFAULT, 163, 140), (DEFAULT, 163, 139), (DEFAULT, 164, 140), (DEFAULT, 164, 139), (DEFAULT, 165, 140), (DEFAULT, 165, 139), (DEFAULT, 166, 140), (DEFAULT, 166, 139), (DEFAULT, 167, 140), (DEFAULT, 167, 139), (DEFAULT, 168, 140), (DEFAULT, 168, 139), (DEFAULT, 169, 140), (DEFAULT, 169, 139), (DEFAULT, 170, 140), (DEFAULT, 170, 139), (DEFAULT, 171, 140), (DEFAULT, 171, 139), (DEFAULT, 172, 140), (DEFAULT, 172, 139), (DEFAULT, 173, 140), (DEFAULT, 173, 139), (DEFAULT, 174, 140), (DEFAULT, 174, 139), (DEFAULT, 175, 140), (DEFAULT, 175, 139), (DEFAULT, 176, 140), (DEFAULT, 176, 139), (DEFAULT, 177, 140), (DEFAULT, 177, 139), (DEFAULT, 178, 140), (DEFAULT, 178, 139), (DEFAULT, 179, 140), (DEFAULT, 179, 139), (DEFAULT, 180, 140), (DEFAULT, 180, 139), (DEFAULT, 181, 140), (DEFAULT, 181, 139), (DEFAULT, 182, 140), (DEFAULT, 182, 139), (DEFAULT, 183, 140), (DEFAULT, 183, 139), (DEFAULT, 184, 140), (DEFAULT, 184, 139), (DEFAULT, 185, 140), (DEFAULT, 185, 139), (DEFAULT, 186, 140), (DEFAULT, 186, 139), (DEFAULT, 187, 140), (DEFAULT, 187, 139), (DEFAULT, 188, 137), (DEFAULT, 188, 139), (DEFAULT, 189, 137), (DEFAULT, 189, 139), (DEFAULT, 190, 137), (DEFAULT, 190, 139), (DEFAULT, 191, 137), (DEFAULT, 191, 139), (DEFAULT, 192, 137), (DEFAULT, 192, 139), (DEFAULT, 193, 137), (DEFAULT, 193, 139), (DEFAULT, 194, 137), (DEFAULT, 194, 139), (DEFAULT, 195, 137), (DEFAULT, 195, 139), (DEFAULT, 196, 137), (DEFAULT, 196, 139), (DEFAULT, 197, 137), (DEFAULT, 197, 139), (DEFAULT, 198, 137), (DEFAULT, 198, 139), (DEFAULT, 199, 137), (DEFAULT, 199, 139), (DEFAULT, 200, 137), (DEFAULT, 200, 139), (DEFAULT, 201, 137), (DEFAULT, 201, 139), (DEFAULT, 202, 137), (DEFAULT, 0, NULL), (DEFAULT, 1, 0), (DEFAULT, 2, 0), (DEFAULT, 63, 0), (DEFAULT, 3, 1), (DEFAULT, 4, 1), (DEFAULT, 64, 63), (DEFAULT, 65, 63), (DEFAULT, 135, 0), (DEFAULT, 136, 0), (DEFAULT, 137, 0), (DEFAULT, 138, 0), (DEFAULT, 139, 0), (DEFAULT, 140, 0), (DEFAULT, 141, 0), (DEFAULT, 142, 0), (DEFAULT, 143, 0), (DEFAULT, 144, 0), (DEFAULT, 145, 0), (DEFAULT, 5, 3), (DEFAULT, 6, 3), (DEFAULT, 7, 3), (DEFAULT, 8, 3), (DEFAULT, 9, 3), (DEFAULT, 10, 3), (DEFAULT, 11, 3), (DEFAULT, 12, 3), (DEFAULT, 13, 3), (DEFAULT, 14, 3), (DEFAULT, 15, 3), (DEFAULT, 16, 3), (DEFAULT, 17, 3), (DEFAULT, 18, 3), (DEFAULT, 19, 3), (DEFAULT, 20, 3), (DEFAULT, 21, 3), (DEFAULT, 22, 3), (DEFAULT, 23, 3), (DEFAULT, 24, 3), (DEFAULT, 25, 3), (DEFAULT, 26, 3), (DEFAULT, 27, 3), (DEFAULT, 28, 3), (DEFAULT, 29, 3), (DEFAULT, 30, 3), (DEFAULT, 31, 3), (DEFAULT, 32, 3), (DEFAULT, 33, 3), (DEFAULT, 34, 3), (DEFAULT, 35, 3), (DEFAULT, 36, 3), (DEFAULT, 37, 3), (DEFAULT, 38, 3), (DEFAULT, 39, 3), (DEFAULT, 40, 3), (DEFAULT, 41, 3), (DEFAULT, 42, 3), (DEFAULT, 43, 3), (DEFAULT, 44, 3), (DEFAULT, 45, 3), (DEFAULT, 46, 3), (DEFAULT, 47, 3), (DEFAULT, 48, 3), (DEFAULT, 49, 3), (DEFAULT, 50, 3), (DEFAULT, 51, 3), (DEFAULT, 52, 3), (DEFAULT, 53, 3), (DEFAULT, 54, 3), (DEFAULT, 55, 2), (DEFAULT, 56, 2), (DEFAULT, 57, 2), (DEFAULT, 58, 2), (DEFAULT, 59, 2), (DEFAULT, 60, 2), (DEFAULT, 61, 2), (DEFAULT, 62, 2), (DEFAULT, 66, 64), (DEFAULT, 67, 66), (DEFAULT, 68, 66), (DEFAULT, 69, 64), (DEFAULT, 70, 69), (DEFAULT, 71, 69), (DEFAULT, 72, 64), (DEFAULT, 73, 72), (DEFAULT, 74, 72), (DEFAULT, 75, 64), (DEFAULT, 76, 75), (DEFAULT, 77, 75), (DEFAULT, 78, 64), (DEFAULT, 79, 78), (DEFAULT, 80, 78), (DEFAULT, 81, 64), (DEFAULT, 82, 81), (DEFAULT, 83, 81), (DEFAULT, 84, 64), (DEFAULT, 85, 84), (DEFAULT, 86, 84), (DEFAULT, 87, 64), (DEFAULT, 88, 87), (DEFAULT, 89, 87), (DEFAULT, 90, 64), (DEFAULT, 91, 90), (DEFAULT, 92, 90), (DEFAULT, 93, 64), (DEFAULT, 94, 93), (DEFAULT, 95, 93), (DEFAULT, 96, 64), (DEFAULT, 97, 96), (DEFAULT, 98, 96), (DEFAULT, 99, 64), (DEFAULT, 100, 99), (DEFAULT, 101, 99), (DEFAULT, 102, 64), (DEFAULT, 103, 102), (DEFAULT, 104, 102), (DEFAULT, 105, 64), (DEFAULT, 106, 105), (DEFAULT, 107, 105), (DEFAULT, 108, 64), (DEFAULT, 109, 108), (DEFAULT, 110, 108), (DEFAULT, 111, 64), (DEFAULT, 112, 111), (DEFAULT, 113, 111), (DEFAULT, 114, 64), (DEFAULT, 115, 114), (DEFAULT, 116, 114), (DEFAULT, 117, 64), (DEFAULT, 118, 117), (DEFAULT, 119, 117), (DEFAULT, 120, 64), (DEFAULT, 121, 120), (DEFAULT, 122, 120), (DEFAULT, 123, 64), (DEFAULT, 124, 123), (DEFAULT, 125, 123), (DEFAULT, 126, 64), (DEFAULT, 127, 126), (DEFAULT, 128, 126), (DEFAULT, 129, 64), (DEFAULT, 130, 129), (DEFAULT, 131, 129), (DEFAULT, 132, 64), (DEFAULT, 133, 132), (DEFAULT, 134, 132), (DEFAULT, 149, 135), (DEFAULT, 150, 135), (DEFAULT, 151, 135), (DEFAULT, 152, 135), (DEFAULT, 153, 135), (DEFAULT, 154, 135), (DEFAULT, 155, 135); +INSERT INTO virtual_instrument.resource_group_to_resource_group VALUES (DEFAULT, 255, 174), (DEFAULT, 256, 175), (DEFAULT, 257, 176), (DEFAULT, 258, 177), (DEFAULT, 259, 178), (DEFAULT, 260, 179), (DEFAULT, 261, 180), (DEFAULT, 262, 181), (DEFAULT, 263, 182), (DEFAULT, 264, 183), (DEFAULT, 265, 184), (DEFAULT, 266, 185), (DEFAULT, 267, 186), (DEFAULT, 268, 187), (DEFAULT, 269, 188), (DEFAULT, 270, 189), (DEFAULT, 271, 190), (DEFAULT, 272, 191), (DEFAULT, 273, 192), (DEFAULT, 274, 193), (DEFAULT, 275, 194), (DEFAULT, 276, 195), (DEFAULT, 277, 196), (DEFAULT, 278, 197), (DEFAULT, 279, 198), (DEFAULT, 280, 199), (DEFAULT, 281, 200), (DEFAULT, 282, 201), (DEFAULT, 283, 202), (DEFAULT, 149, 138), (DEFAULT, 150, 138), (DEFAULT, 151, 138), (DEFAULT, 152, 138), (DEFAULT, 153, 138), (DEFAULT, 154, 138), (DEFAULT, 155, 141), (DEFAULT, 156, 141), (DEFAULT, 157, 141), (DEFAULT, 158, 141), (DEFAULT, 159, 141), (DEFAULT, 160, 141), (DEFAULT, 161, 141), (DEFAULT, 162, 141), (DEFAULT, 163, 141), (DEFAULT, 164, 141), (DEFAULT, 165, 141), (DEFAULT, 166, 141), (DEFAULT, 167, 141), (DEFAULT, 168, 141), (DEFAULT, 169, 141), (DEFAULT, 170, 141), (DEFAULT, 171, 141), (DEFAULT, 172, 141), (DEFAULT, 149, 143), (DEFAULT, 150, 143), (DEFAULT, 151, 143), (DEFAULT, 152, 143), (DEFAULT, 153, 143), (DEFAULT, 154, 143), (DEFAULT, 156, 143), (DEFAULT, 157, 143), (DEFAULT, 158, 143), (DEFAULT, 159, 143), (DEFAULT, 165, 143), (DEFAULT, 149, 144), (DEFAULT, 150, 144), (DEFAULT, 151, 144), (DEFAULT, 152, 144), (DEFAULT, 153, 144), (DEFAULT, 154, 144), (DEFAULT, 155, 144), (DEFAULT, 156, 144), (DEFAULT, 157, 144), (DEFAULT, 158, 144), (DEFAULT, 159, 144), (DEFAULT, 160, 144), (DEFAULT, 161, 144), (DEFAULT, 162, 144), (DEFAULT, 163, 144), (DEFAULT, 164, 144), (DEFAULT, 165, 144), (DEFAULT, 166, 144), (DEFAULT, 168, 144), (DEFAULT, 169, 144), (DEFAULT, 171, 144), (DEFAULT, 172, 144), (DEFAULT, 172, 139), (DEFAULT, 149, 140), (DEFAULT, 149, 139), (DEFAULT, 150, 140), (DEFAULT, 150, 139), (DEFAULT, 151, 140), (DEFAULT, 151, 139), (DEFAULT, 152, 140), (DEFAULT, 152, 139), (DEFAULT, 153, 140), (DEFAULT, 153, 139), (DEFAULT, 154, 140), (DEFAULT, 154, 139), (DEFAULT, 155, 140), (DEFAULT, 155, 139), (DEFAULT, 156, 140), (DEFAULT, 156, 139), (DEFAULT, 157, 140), (DEFAULT, 157, 139), (DEFAULT, 158, 140), (DEFAULT, 158, 139), (DEFAULT, 159, 140), (DEFAULT, 159, 139), (DEFAULT, 160, 140), (DEFAULT, 160, 139), (DEFAULT, 161, 140), (DEFAULT, 161, 139), (DEFAULT, 162, 140), (DEFAULT, 162, 139), (DEFAULT, 163, 140), (DEFAULT, 163, 139), (DEFAULT, 164, 140), (DEFAULT, 164, 139), (DEFAULT, 165, 140), (DEFAULT, 165, 139), (DEFAULT, 166, 140), (DEFAULT, 166, 139), (DEFAULT, 167, 140), (DEFAULT, 167, 139), (DEFAULT, 168, 140), (DEFAULT, 168, 139), (DEFAULT, 169, 140), (DEFAULT, 169, 139), (DEFAULT, 170, 140), (DEFAULT, 170, 139), (DEFAULT, 171, 140), (DEFAULT, 171, 139), (DEFAULT, 172, 140), (DEFAULT, 172, 139), (DEFAULT, 173, 140), (DEFAULT, 173, 139), (DEFAULT, 174, 140), (DEFAULT, 174, 139), (DEFAULT, 175, 140), (DEFAULT, 175, 139), (DEFAULT, 176, 140), (DEFAULT, 176, 139), (DEFAULT, 177, 140), (DEFAULT, 177, 139), (DEFAULT, 178, 140), (DEFAULT, 178, 139), (DEFAULT, 179, 140), (DEFAULT, 179, 139), (DEFAULT, 180, 140), (DEFAULT, 180, 139), (DEFAULT, 181, 140), (DEFAULT, 181, 139), (DEFAULT, 182, 140), (DEFAULT, 182, 139), (DEFAULT, 183, 140), (DEFAULT, 183, 139), (DEFAULT, 184, 140), (DEFAULT, 184, 139), (DEFAULT, 185, 140), (DEFAULT, 185, 139), (DEFAULT, 186, 140), (DEFAULT, 186, 139), (DEFAULT, 187, 140), (DEFAULT, 187, 139), (DEFAULT, 188, 137), (DEFAULT, 188, 139), (DEFAULT, 189, 137), (DEFAULT, 189, 139), (DEFAULT, 190, 137), (DEFAULT, 190, 139), (DEFAULT, 191, 137), (DEFAULT, 191, 139), (DEFAULT, 192, 137), (DEFAULT, 192, 139), (DEFAULT, 193, 137), (DEFAULT, 193, 139), (DEFAULT, 194, 137), (DEFAULT, 194, 139), (DEFAULT, 195, 137), (DEFAULT, 195, 139), (DEFAULT, 196, 137), (DEFAULT, 196, 139), (DEFAULT, 197, 137), (DEFAULT, 197, 139), (DEFAULT, 198, 137), (DEFAULT, 198, 139), (DEFAULT, 199, 137), (DEFAULT, 199, 139), (DEFAULT, 200, 137), (DEFAULT, 200, 139), (DEFAULT, 201, 137), (DEFAULT, 201, 139), (DEFAULT, 202, 137), (DEFAULT, 0, NULL), (DEFAULT, 1, 0), (DEFAULT, 2, 0), (DEFAULT, 63, 0), (DEFAULT, 3, 1), (DEFAULT, 4, 1), (DEFAULT, 64, 63), (DEFAULT, 65, 63), (DEFAULT, 135, 0), (DEFAULT, 136, 0), (DEFAULT, 137, 0), (DEFAULT, 138, 0), (DEFAULT, 139, 0), (DEFAULT, 140, 0), (DEFAULT, 141, 0), (DEFAULT, 142, 0), (DEFAULT, 143, 0), (DEFAULT, 144, 0), (DEFAULT, 145, 0), (DEFAULT, 5, 3), (DEFAULT, 6, 3), (DEFAULT, 7, 3), (DEFAULT, 8, 3), (DEFAULT, 9, 3), (DEFAULT, 10, 3), (DEFAULT, 11, 3), (DEFAULT, 12, 3), (DEFAULT, 13, 3), (DEFAULT, 14, 3), (DEFAULT, 15, 3), (DEFAULT, 16, 3), (DEFAULT, 17, 3), (DEFAULT, 18, 3), (DEFAULT, 19, 3), (DEFAULT, 20, 3), (DEFAULT, 21, 3), (DEFAULT, 22, 3), (DEFAULT, 23, 3), (DEFAULT, 24, 3), (DEFAULT, 25, 3), (DEFAULT, 26, 3), (DEFAULT, 27, 3), (DEFAULT, 28, 3), (DEFAULT, 29, 3), (DEFAULT, 30, 3), (DEFAULT, 31, 3), (DEFAULT, 32, 3), (DEFAULT, 33, 3), (DEFAULT, 34, 3), (DEFAULT, 35, 3), (DEFAULT, 36, 3), (DEFAULT, 37, 3), (DEFAULT, 38, 3), (DEFAULT, 39, 3), (DEFAULT, 40, 3), (DEFAULT, 41, 3), (DEFAULT, 42, 3), (DEFAULT, 43, 3), (DEFAULT, 44, 3), (DEFAULT, 45, 3), (DEFAULT, 46, 3), (DEFAULT, 47, 3), (DEFAULT, 48, 3), (DEFAULT, 49, 3), (DEFAULT, 50, 3), (DEFAULT, 51, 3), (DEFAULT, 52, 3), (DEFAULT, 53, 3), (DEFAULT, 54, 3), (DEFAULT, 55, 2), (DEFAULT, 56, 2), (DEFAULT, 57, 2), (DEFAULT, 58, 2), (DEFAULT, 59, 2), (DEFAULT, 60, 2), (DEFAULT, 61, 2), (DEFAULT, 62, 2), (DEFAULT, 66, 64), (DEFAULT, 67, 66), (DEFAULT, 68, 66), (DEFAULT, 69, 64), (DEFAULT, 70, 69), (DEFAULT, 71, 69), (DEFAULT, 72, 64), (DEFAULT, 73, 72), (DEFAULT, 74, 72), (DEFAULT, 75, 64), (DEFAULT, 76, 75), (DEFAULT, 77, 75), (DEFAULT, 78, 64), (DEFAULT, 79, 78), (DEFAULT, 80, 78), (DEFAULT, 81, 64), (DEFAULT, 82, 81), (DEFAULT, 83, 81), (DEFAULT, 84, 64), (DEFAULT, 85, 84), (DEFAULT, 86, 84), (DEFAULT, 87, 64), (DEFAULT, 88, 87), (DEFAULT, 89, 87), (DEFAULT, 90, 64), (DEFAULT, 91, 90), (DEFAULT, 92, 90), (DEFAULT, 93, 64), (DEFAULT, 94, 93), (DEFAULT, 95, 93), (DEFAULT, 96, 64), (DEFAULT, 97, 96), (DEFAULT, 98, 96), (DEFAULT, 99, 64), (DEFAULT, 100, 99), (DEFAULT, 101, 99), (DEFAULT, 102, 64), (DEFAULT, 103, 102), (DEFAULT, 104, 102), (DEFAULT, 105, 64), (DEFAULT, 106, 105), (DEFAULT, 107, 105), (DEFAULT, 108, 64), (DEFAULT, 109, 108), (DEFAULT, 110, 108), (DEFAULT, 111, 64), (DEFAULT, 112, 111), (DEFAULT, 113, 111), (DEFAULT, 114, 64), (DEFAULT, 115, 114), (DEFAULT, 116, 114), (DEFAULT, 117, 64), (DEFAULT, 118, 117), (DEFAULT, 119, 117), (DEFAULT, 120, 64), (DEFAULT, 121, 120), (DEFAULT, 122, 120), (DEFAULT, 123, 64), (DEFAULT, 124, 123), (DEFAULT, 125, 123), (DEFAULT, 126, 64), (DEFAULT, 127, 126), (DEFAULT, 128, 126), (DEFAULT, 129, 64), (DEFAULT, 130, 129), (DEFAULT, 131, 129), (DEFAULT, 132, 64), (DEFAULT, 133, 132), (DEFAULT, 134, 132), (DEFAULT, 149, 135), (DEFAULT, 150, 135), (DEFAULT, 151, 135), (DEFAULT, 152, 135), (DEFAULT, 153, 135), (DEFAULT, 154, 135), (DEFAULT, 155, 135), (DEFAULT, 156, 135), (DEFAULT, 157, 135), (DEFAULT, 158, 135), (DEFAULT, 159, 135), (DEFAULT, 160, 135), (DEFAULT, 161, 135), (DEFAULT, 162, 135), (DEFAULT, 163, 135), (DEFAULT, 164, 135), (DEFAULT, 165, 135), (DEFAULT, 166, 135), (DEFAULT, 167, 135), (DEFAULT, 168, 135), (DEFAULT, 169, 135), (DEFAULT, 170, 135), (DEFAULT, 171, 135), (DEFAULT, 172, 135), (DEFAULT, 173, 136), (DEFAULT, 174, 136), (DEFAULT, 175, 136), (DEFAULT, 176, 136), (DEFAULT, 177, 136), (DEFAULT, 178, 136), (DEFAULT, 179, 136), (DEFAULT, 180, 136), (DEFAULT, 181, 136), (DEFAULT, 182, 136), (DEFAULT, 183, 136), (DEFAULT, 184, 136), (DEFAULT, 185, 136), (DEFAULT, 186, 136), (DEFAULT, 187, 136), (DEFAULT, 188, 137), (DEFAULT, 189, 137), (DEFAULT, 190, 137), (DEFAULT, 191, 137), (DEFAULT, 192, 137), (DEFAULT, 193, 137), (DEFAULT, 194, 137), (DEFAULT, 195, 137), (DEFAULT, 196, 137), (DEFAULT, 197, 137), (DEFAULT, 198, 137), (DEFAULT, 199, 137), (DEFAULT, 200, 137), (DEFAULT, 201, 137), (DEFAULT, 202, 137), (DEFAULT, 206, 149), (DEFAULT, 207, 149), (DEFAULT, 208, 150), (DEFAULT, 209, 150), (DEFAULT, 210, 151), (DEFAULT, 211, 151), (DEFAULT, 212, 152), (DEFAULT, 213, 152), (DEFAULT, 214, 153), (DEFAULT, 215, 153), (DEFAULT, 216, 154), (DEFAULT, 217, 154), (DEFAULT, 218, 155), (DEFAULT, 219, 155), (DEFAULT, 220, 156), (DEFAULT, 221, 156), (DEFAULT, 222, 157), (DEFAULT, 223, 157), (DEFAULT, 224, 158), (DEFAULT, 225, 158), (DEFAULT, 226, 159), (DEFAULT, 227, 159), (DEFAULT, 228, 160), (DEFAULT, 229, 160), (DEFAULT, 230, 161), (DEFAULT, 231, 161), (DEFAULT, 232, 162), (DEFAULT, 233, 162), (DEFAULT, 234, 163), (DEFAULT, 235, 163), (DEFAULT, 236, 164), (DEFAULT, 237, 164), (DEFAULT, 238, 165), (DEFAULT, 239, 165), (DEFAULT, 240, 166), (DEFAULT, 241, 166), (DEFAULT, 242, 167), (DEFAULT, 243, 167), (DEFAULT, 244, 168), (DEFAULT, 245, 168), (DEFAULT, 246, 169), (DEFAULT, 247, 169), (DEFAULT, 248, 170), (DEFAULT, 249, 170), (DEFAULT, 250, 171), (DEFAULT, 251, 171), (DEFAULT, 252, 172), (DEFAULT, 253, 172), (DEFAULT, 254, 173); COMMIT; diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/create_database.sql b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/create_database.sql index 13332c92d7f9fea9875a44ff0288b81d223d082e..40337a9dc5f0bebe742f0ca55cda5d47dd44d6c9 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/create_database.sql +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/radb/sql/create_database.sql @@ -251,6 +251,15 @@ CREATE INDEX resource_claim_resource_id_idx CREATE INDEX resource_claim_status_id_idx ON resource_allocation.resource_claim (status_id); +CREATE TABLE resource_allocation._rebuild_usages_active_claims ( LIKE resource_allocation.resource_claim INCLUDING INDEXES INCLUDING CONSTRAINTS ); +DROP INDEX resource_allocation._rebuild_usages_active_claims_resource_id_idx; --remove unnecessary index +DROP INDEX resource_allocation._rebuild_usages_active_claims_status_id_idx; --remove unnecessary index +DROP INDEX resource_allocation._rebuild_usages_active_claims_task_id_idx; --remove unnecessary index +ALTER TABLE resource_allocation._rebuild_usages_active_claims + OWNER TO resourceassignment; +COMMENT ON TABLE resource_allocation._rebuild_usages_active_claims + IS 'helper table for the rebuild_resource_usages_from_claims_for_resource_of_status method.'; + CREATE TABLE resource_allocation.conflict_reason ( id serial NOT NULL, reason text NOT NULL, @@ -350,6 +359,14 @@ CREATE INDEX resource_usage_resource_id_idx CREATE INDEX resource_usage_status_id_idx ON resource_allocation.resource_usage (status_id); +CREATE TABLE resource_allocation._rebuild_usages_active_usages ( LIKE resource_allocation.resource_usage INCLUDING INDEXES INCLUDING CONSTRAINTS ); +DROP INDEX resource_allocation._rebuild_usages_active_usages_status_id_idx; --remove unnecessary index +DROP INDEX resource_allocation._rebuild_usages_active_usages_resource_id_idx; --remove unnecessary index +ALTER TABLE resource_allocation._rebuild_usages_active_usages + OWNER TO resourceassignment; +COMMENT ON TABLE resource_allocation._rebuild_usages_active_usages + IS 'helper table for the rebuild_resource_usages_from_claims_for_resource_of_status method.'; + CREATE TABLE resource_monitoring.resource_availability ( id serial NOT NULL, resource_id integer NOT NULL REFERENCES virtual_instrument.resource ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE, diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/radb_performance_test.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/radb_performance_test.py new file mode 100755 index 0000000000000000000000000000000000000000..300e97077358c0d3824f45b153b4a922a2e22a9c --- /dev/null +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/radb_performance_test.py @@ -0,0 +1,113 @@ +#!/usr/bin/python + +# Copyright (C) 2012-2015 ASTRON (Netherlands Institute for Radio Astronomy) +# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite 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, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite 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 the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. + +# $Id: $ +from optparse import OptionParser +import os +from datetime import datetime, timedelta +import logging +from random import randint + +logger = logging.getLogger(__name__) + +from lofar.common import dbcredentials +from lofar.sas.resourceassignment.database.radb import RADatabase +from lofar.common.datetimeutils import totalSeconds + +def test_resource_usages_performance(radb): + radb.updateResourceAvailability(117, available_capacity=10000000, total_capacity=10000000) + + now = datetime.utcnow() + now -= timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to full hour + spec_ids = [] + filename = 'resource_usages_performance%s.csv' % (datetime.utcnow().strftime('%Y%m%dT%H%M%S'),) + with open(filename, 'w') as file: + file.write('#claims, elapsed_insert, elapsed_rebuild\n') + counter = 0 + for k in range(20): + num_claims_to_insert = 20 + num_insert_repeats = 10 + elapsed_insert = 0 + for i in range(num_insert_repeats): + counter += 1 + result = radb.insertSpecificationAndTask(counter, counter, 'approved', 'observation', + now+timedelta(hours=3*counter), + now + timedelta(hours=1 + 3*counter), + 'content', 'CEP4') + task_id = result['task_id'] + task = radb.getTask(task_id) + spec_ids.append(task['specification_id']) + + claims = [{'resource_id': 117, + 'starttime': task['starttime']-timedelta(minutes=randint(0, 1800)), + 'endtime': task['starttime']+timedelta(seconds=randint(1801, 3600)), + 'status': 'tentative', + 'claim_size': q} + for q in range(num_claims_to_insert)] + + start = datetime.utcnow() + radb.insertResourceClaims(task_id, claims, 'foo', 1, 1) + elapsed_insert += totalSeconds(datetime.utcnow() - start) + elapsed_insert /= 10 + + start = datetime.utcnow() + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + radb.rebuild_resource_usages_from_claims(117, 'tentative') + elapsed_rebuild = totalSeconds(datetime.utcnow() - start) + + logger.info('TEST RESULT: radb now contains %d claims, insert of %d claims takes on average %.3fsec and a rebuild of the whole usage table takes %.3fsec', + len(radb.getResourceClaims()), num_claims_to_insert, elapsed_insert, elapsed_rebuild) + file.write('%d, %.3f, %.3f\n' % (len(radb.getResourceClaims()), elapsed_insert, elapsed_rebuild)) + file.flush() + + logger.info('removing all test specs/tasks/claims from db') + + for spec_id in spec_ids: + radb.deleteSpecification(spec_id) + + logger.info('Done. Results can be found in file: %s', filename) + +if __name__ == '__main__': + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',level=logging.INFO) + + # Check the invocation arguments + parser = OptionParser("%prog [options]", description='runs some test queries on the radb') + parser.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose logging') + parser.add_option_group(dbcredentials.options_group(parser)) + parser.set_defaults(dbcredentials="RADB") + (options, args) = parser.parse_args() + + dbcreds = dbcredentials.parse_options(options) + + print + print 'Using dbcreds: %s' % dbcreds.stringWithHiddenPassword() + print 'Are you sure you want to run the performance tests on this database? Tables will be modified! Precious data might be lost!' + print 'This test gives the most reproducable results when run on a clean database.' + print + answer = raw_input('CONTINUE? y/<n>: ') + if 'y' not in answer.lower(): + print 'Exiting without running the test...' + exit(1) + + print 'Starting test....' + radb = RADatabase(dbcreds=dbcreds, log_queries=options.verbose) + + test_resource_usages_performance(radb) + + diff --git a/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.py b/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.py index 9c04e2b22654de6f1137a9e6d606a6165e0f48e0..4016c7b8766ab91a1958fd8299c7a594bc792f3f 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.py +++ b/SAS/ResourceAssignment/ResourceAssignmentDatabase/tests/t_radb.py @@ -25,6 +25,7 @@ import os from datetime import datetime, timedelta from dateutil import parser import logging +from pprint import pformat logger = logging.getLogger(__name__) @@ -159,7 +160,7 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): # --- tests start here - # + # integrity tests of postgres database itself # # Note: These are meant to make sure the setup generally works and all sql scripts were applied. @@ -998,24 +999,27 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertEqual(len(specifications), 2) def test_getSpecification_normal_use_select_one_succeeds(self): - """ Verify if radb.getSpecification() returns a list containing a single specification """ + """ Verify if radb.getSpecification() returns a single single specification """ spec_ids = [self._insert_test_spec(), self._insert_test_spec(), self._insert_test_spec()] - specifications = self.radb.getSpecification(spec_ids[1]) + specification = self.radb.getSpecification(spec_ids[1]) - self.assertEqual(len(specifications), 1) + self.assertTrue(specification) + self.assertEqual(spec_ids[1], specification['id']) def test_task_and_claim_conflicts(self): # TODO: split up once the test setup is faster (not creating a new db for each test method) - # for testing purposous let's give CEP4 storage a total size of 100 - self.assertTrue(self.radb.updateResourceAvailability(117, available_capacity=100, total_capacity=100)) - self.assertEqual(100, self.radb.getResources(117, include_availability=True)[0]['total_capacity']) + # for testing purposes let's give CEP4 storage a total size of 100 + cep4_id = 117 + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100, total_capacity=100)) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) now = datetime.utcnow() - now -= timedelta(seconds=now.second, microseconds=now.microsecond) + now -= timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to full hour - result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now+timedelta(hours=1), 'foo', 'CEP4') + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now+timedelta(hours=1), + 'foo', 'CEP4') self.assertTrue(result['inserted']) spec_id1 = result['specification_id'] task_id1 = result['task_id'] @@ -1024,7 +1028,7 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertTrue(task1) self.assertEqual(task_id1, task1['id']) - t1_claim1 = { 'resource_id': 117, + t1_claim1 = { 'resource_id': cep4_id, 'starttime': task1['starttime'], 'endtime': task1['endtime'], 'status': 'tentative', @@ -1049,22 +1053,23 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertEqual(value, t1_claims[0][key]) # try to insert a claim with the wrong (already 'claimed') status. Should rollback, and return no ids. - t1_claim2 = { 'resource_id': 117, + t1_claim2 = { 'resource_id': cep4_id, 'starttime': task1['starttime'], 'endtime': task1['endtime'], 'status': 'claimed', 'claim_size': 10 } t1_faulty_claim_ids = self.radb.insertResourceClaims(task_id1, [t1_claim2], 'foo', 1, 1) - self.assertEqual(0, len(t1_faulty_claim_ids)) + self.assertEqual(1, len(self.radb.getResourceClaims(task_ids=task_id1))) #there should still be one (proper/non-faulty) claim for this task # try to insert a claim with the wrong (already 'conflict') status. Should rollback, and return no ids. - t1_claim3 = { 'resource_id': 117, + t1_claim3 = { 'resource_id': cep4_id, 'starttime': task1['starttime'], 'endtime': task1['endtime'], 'status': 'conflict', 'claim_size': 10 } t1_faulty_claim_ids = self.radb.insertResourceClaims(task_id1, [t1_claim3], 'foo', 1, 1) - self.assertEqual(0, len(t1_faulty_claim_ids)) + t1_faulty_claim_ids = self.radb.insertResourceClaims(task_id1, [t1_claim2], 'foo', 1, 1) + self.assertEqual(1, len(self.radb.getResourceClaims(task_ids=task_id1))) #there should still be one (proper/non-faulty) claim for this task # try to update the task status to scheduled, should not succeed, since it's claims are not 'claimed' yet. self.assertFalse(self.radb.updateTask(task_id1, task_status='scheduled')) @@ -1078,12 +1083,11 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertTrue(self.radb.updateTask(task_id1, task_status='scheduled')) self.assertEqual('scheduled', self.radb.getTask(task_id1)['status']) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_claims(t1_claim_ids[0]))) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_tasks(t1_claim_ids[0]))) + self.assertEqual(0, len(self.radb.get_overlapping_claims(t1_claim_ids[0]))) + self.assertEqual(0, len(self.radb.get_overlapping_tasks(t1_claim_ids[0]))) - self.assertEqual(40, self.radb.get_max_resource_usage_between(117, task1['starttime'], task1['starttime'], 'claimed')['usage']) - - self.assertEqual(0, self.radb.get_max_resource_usage_between(117, task1['starttime']-timedelta(hours=2), task1['starttime']-timedelta(hours=1), 'claimed')['usage']) + self.assertEqual(40, self.radb.get_max_resource_usage_between(cep4_id, task1['starttime'], task1['starttime'], 'claimed')['usage']) + self.assertEqual(0, self.radb.get_max_resource_usage_between(cep4_id, task1['starttime']-timedelta(hours=2), task1['starttime']-timedelta(hours=1), 'claimed')['usage']) logger.info('------------------------------ concludes task 1 ------------------------------') logger.info('-- now test with a 2nd task, and test resource availability, conflicts etc. --') @@ -1098,7 +1102,7 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertTrue(task2) # insert a claim which won't fit, claim status after insert should be 'conflict' instead of 'tentative' - t2_claim1 = { 'resource_id': 117, + t2_claim1 = { 'resource_id': cep4_id, 'starttime': task2['starttime'], 'endtime': task2['endtime'], 'status': 'tentative', @@ -1113,11 +1117,16 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): # and the task's status should be conflict as well self.assertEqual('conflict', self.radb.getTask(task_id2)['status']) - self.assertEqual(set([t1_claim_ids[0]]), set(c['id'] for c in - self.radb.get_conflicting_overlapping_claims(t2_claim_ids[0]))) - self.assertEqual(set([task_id1]), set(t['id'] for t in - self.radb.get_conflicting_overlapping_tasks(t2_claim_ids[0]))) + self.assertEqual(set([t1_claim_ids[0]]), set(c['id'] for c in self.radb.get_overlapping_claims(t2_claim_ids[0]))) + self.assertEqual(set([task_id1]), set(t['id'] for t in self.radb.get_overlapping_tasks(t2_claim_ids[0]))) + + #try to connect this claim to task1, should fail + self.assertFalse(self.radb.updateResourceClaims(t2_claim_ids, task_id=task_id1)) + self.assertEqual(task_id2, t2_claims[0]['task_id']) + #try to connect this claim to other resource, should fail + self.assertFalse(self.radb.updateResourceClaims(t2_claim_ids, resource_id=118)) + self.assertEqual(cep4_id, t2_claims[0]['resource_id']) # try to update the task status to scheduled, should not succeed, since it's claims are not 'claimed' yet. self.assertFalse(self.radb.updateTask(task_id2, task_status='scheduled')) @@ -1128,13 +1137,14 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertEqual('conflict', self.radb.getResourceClaim(t2_claim_ids[0])['status']) # do conflict resolution, shift task and claims - self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=now+timedelta(hours=2), endtime=now+timedelta(hours=3))) + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=now+timedelta(hours=2), + endtime=now+timedelta(hours=3))) # now the task and claim status should not be conflict anymore self.assertEqual('tentative', self.radb.getResourceClaim(t2_claim_ids[0])['status']) self.assertEqual('approved', self.radb.getTask(task_id2)['status']) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_claims(t2_claim_ids[0]))) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_tasks(t2_claim_ids[0]))) + self.assertEqual(0, len(self.radb.get_overlapping_claims(t2_claim_ids[0]))) + self.assertEqual(0, len(self.radb.get_overlapping_tasks(t2_claim_ids[0]))) # try to update the claim status to claimed, should succeed now self.assertTrue(self.radb.updateResourceClaims(t2_claim_ids, status='claimed')) @@ -1144,8 +1154,50 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertTrue(self.radb.updateTask(task_id2, task_status='scheduled')) self.assertEqual('scheduled', self.radb.getTask(task_id2)['status']) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_claims(t2_claim_ids[0]))) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_tasks(t2_claim_ids[0]))) + self.assertEqual(0, len(self.radb.get_overlapping_claims(t2_claim_ids[0]))) + self.assertEqual(0, len(self.radb.get_overlapping_tasks(t2_claim_ids[0]))) + + # updating task/claim start/endtime should work, even for scheduled tasks with claimed claims + # effect might be that a scheduled tasks goes to conflict + # force conflict by moving back to original start/endtimes + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=task2['starttime'], endtime=task2['endtime'])) + self.assertEqual('conflict', self.radb.getResourceClaim(t2_claim_ids[0])['status']) + self.assertEqual('conflict', self.radb.getTask(task_id2)['status']) + + # again do conflict resolution, shift task and claims + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=now+timedelta(hours=2), endtime=now+timedelta(hours=3))) + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, claim_status='claimed', task_status='scheduled')) + # now the task and claim status should be scheduled/claimed + self.assertEqual('scheduled', self.radb.getTask(task_id2)['status']) + self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status']) + + # updating task/claim start/endtime should work, even for scheduled tasks with claimed claims + # effect might be that a scheduled tasks goes to conflict + # now, make simple endtime adjustment, task should stay scheduled + + logger.info("resource usages:\n%s", pformat(self.radb.getResourceUsages(now-timedelta(days=1.0), now+timedelta(days=2.0), cep4_id))) + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, endtime=now+timedelta(hours=2.75))) + logger.info("resource usages:\n%s", pformat(self.radb.getResourceUsages(now-timedelta(days=1.0), now+timedelta(days=2.0), cep4_id))) + + # now the task and claim status should still be scheduled/claimed + self.assertEqual('scheduled', self.radb.getTask(task_id2)['status']) + self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status']) + + # now some weird corner case... + # when a task is > queued (for example, finished) + # then we don't want conflict statuses anymore if we update start/endtimes + # test here with weird starttime shift back to overlap with task1 + self.assertTrue(self.radb.updateTask(task_id2, task_status='finished')) + self.assertEqual('finished', self.radb.getTask(task_id2)['status']) + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=task1['starttime'])) + self.assertEqual('finished', self.radb.getTask(task_id2)['status']) + self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status']) + + #ok, that works, now set the start/end time back to 'normal' for some later test cases + self.assertTrue(self.radb.updateTaskAndResourceClaims(task_id2, starttime=now+timedelta(hours=2), endtime=now+timedelta(hours=3))) + self.assertEqual('finished', self.radb.getTask(task_id2)['status']) + self.assertEqual('claimed', self.radb.getResourceClaim(t2_claim_ids[0])['status']) + logger.info('------------------------------ concludes task 2 ------------------------------') logger.info('-- now test with a 3rd task, and test resource availability, conflicts etc. --') @@ -1167,7 +1219,7 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertTrue(task3) # insert a claim which won't fit, claim status after insert should be 'conflict' instead of 'tentative' - t3_claim1 = { 'resource_id': 117, + t3_claim1 = { 'resource_id': cep4_id, 'starttime': task3['starttime'], 'endtime': task3['endtime'], 'status': 'tentative', @@ -1182,9 +1234,9 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): # and the task's status should be conflict as well self.assertEqual('conflict', self.radb.getTask(task_id3)['status']) self.assertEqual(set([t1_claim_ids[0], t2_claim_ids[0]]), set(c['id'] for c in - self.radb.get_conflicting_overlapping_claims(t3_claim_ids[0]))) + self.radb.get_overlapping_claims(t3_claim_ids[0]))) self.assertEqual(set([task_id1, task_id2]), set(t['id'] for t in - self.radb.get_conflicting_overlapping_tasks(t3_claim_ids[0]))) + self.radb.get_overlapping_tasks(t3_claim_ids[0]))) # try to update the task status to scheduled, should not succeed, since it's claims are not 'claimed' yet. self.assertFalse(self.radb.updateTask(task_id3, task_status='scheduled')) @@ -1202,9 +1254,9 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertEqual('conflict', self.radb.getTask(task_id3)['status']) self.assertEqual(set([t2_claim_ids[0]]), set(c['id'] for c in - self.radb.get_conflicting_overlapping_claims(t3_claim_ids[0]))) + self.radb.get_overlapping_claims(t3_claim_ids[0]))) self.assertEqual(set([task_id2]), set(t['id'] for t in - self.radb.get_conflicting_overlapping_tasks(t3_claim_ids[0]))) + self.radb.get_overlapping_tasks(t3_claim_ids[0]))) # do conflict resolution, reduce claim size (but keep overlapping with task2) self.assertTrue(self.radb.updateResourceClaim(t3_claim_ids[0], claim_size=5)) @@ -1213,8 +1265,9 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertEqual('tentative', self.radb.getResourceClaim(t3_claim_ids[0])['status']) self.assertEqual('approved', self.radb.getTask(task_id3)['status']) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_claims(t3_claim_ids[0]))) - self.assertEqual(0, len(self.radb.get_conflicting_overlapping_tasks(t3_claim_ids[0]))) + self.assertEqual(1, len(self.radb.get_overlapping_claims(t3_claim_ids[0]))) + self.assertEqual(1, len(self.radb.get_overlapping_tasks(t3_claim_ids[0]))) + self.assertEqual(task2['id'], self.radb.get_overlapping_tasks(t3_claim_ids[0])[0]['id']) # try to update the claim status to claimed, should succeed now self.assertTrue(self.radb.updateResourceClaims(t3_claim_ids, status='claimed')) @@ -1232,16 +1285,877 @@ class ResourceAssignmentDatabaseTest(unittest.TestCase): self.assertEqual('claimed', self.radb.getResourceClaim(t3_claim_ids[0])['status']) self.assertEqual('scheduled', self.radb.getTask(task_id3)['status']) + # suppose the resource_usages table is broken for some reason, fix it.... # break it first... self._execute_query('TRUNCATE TABLE resource_allocation.resource_usage;') #check that it's broken - self.assertNotEqual(40, self.radb.get_max_resource_usage_between(117, task1['starttime'], task1['starttime'], 'claimed')['usage']) + self.assertNotEqual(40, self.radb.get_max_resource_usage_between(cep4_id, task1['starttime'], task1['starttime'], 'claimed')['usage']) #fix it - self.radb.rebuild_resource_usages_table_from_claims() + self.radb.rebuild_resource_usages_from_claims() #and test again that it's ok - self.assertEqual(40, self.radb.get_max_resource_usage_between(117, task1['starttime'], task1['starttime'], 'claimed')['usage']) - self.assertEqual(0, self.radb.get_max_resource_usage_between(117, task1['starttime']-timedelta(hours=2), task1['starttime']-timedelta(hours=1), 'claimed')['usage']) + self.assertEqual(40, self.radb.get_max_resource_usage_between(cep4_id, task1['starttime'], task1['starttime'], 'claimed')['usage']) + self.assertEqual(0, self.radb.get_max_resource_usage_between(cep4_id, task1['starttime']-timedelta(hours=2), task1['starttime']-timedelta(hours=1), 'claimed')['usage']) + + def test_resource_usages(self): + # for testing purposous let's give CEP4 storage a total size of 100 + cep4_id = 117 + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100, total_capacity=100)) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + + now = datetime.utcnow() + now -= timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to full hour + + # insert one task, and reuse that for multiple claims + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now + timedelta(hours=1), + 'content', 'CEP4') + self.assertTrue(result['inserted']) + task_id = result['task_id'] + task = self.radb.getTask(task_id) + self.assertTrue(task) + + # insert a few claims one after the other, and check everything again and again in each intermediate step + # because there are various special cases coded below where claims overlap/touch/etc which all need to be checked. + + # insert a claim, and check the usages for various timestamps + claim1 = {'resource_id': cep4_id, 'starttime': now+timedelta(minutes=0), 'endtime': now+timedelta(minutes=10), 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim1], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + # insert another non-overlapping claim, and check the usages for various timestamps + claim2 = {'resource_id': cep4_id, 'starttime': now+timedelta(minutes=20), 'endtime': now+timedelta(minutes=30), 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim2], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + # insert another claim which overlaps with both claim1 and claim2, and check the usages for various timestamps + claim3 = {'resource_id': cep4_id, 'starttime': now+timedelta(minutes=5), 'endtime': now+timedelta(minutes=25), 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim3], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['starttime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + + # insert another claim which overlaps with claim1 and ends at the same endtime as claim3, and check the usages for various timestamps + claim4 = {'resource_id': cep4_id, 'starttime': now+timedelta(minutes=7.5), 'endtime': claim3['endtime'], 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim4], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['endtime'], 'tentative')['usage']) #c4_endtime should be equal to c3_endtime + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['endtime'], 'tentative')['usage']) #so usage should drop by 2*1 at this timestamp + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + # insert another claim which starts when claim1 ends and last 1 minute, and check the usages for various timestamps + claim5 = {'resource_id': cep4_id, 'starttime': claim1['endtime'], 'endtime': claim1['endtime']+timedelta(minutes=1), 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim5], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) #drops by 1 because c1 ends, but climbs by 1 because c5 starts + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['starttime'], 'tentative')['usage']) #drops by 1 because c1 ends, but climbs by 1 because c5 starts + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['endtime'], 'tentative')['usage']) #drops by 1 because c5 ends + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['endtime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + # last edge case, insert another claim which starts when first claim starts, and end when last claim ends. Should lift all usages by 1 (except outer ones). + claim6 = {'resource_id': cep4_id, 'starttime': claim1['starttime'], 'endtime': claim2['endtime'], 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim6], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['starttime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['starttime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['endtime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['endtime'], 'tentative')['usage']) #c4_endtime should be equal to c3_endtime + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['endtime'], 'tentative')['usage']) #so usage should drop by 2*1 at this timestamp + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + # conclude with two simple cases, + # first final simple case: insert another claim follows (non overlapping/non-touching) all others + claim7 = {'resource_id': cep4_id, 'starttime': claim2['endtime']+timedelta(minutes=10), 'endtime': claim2['endtime']+timedelta(minutes=20), 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim7], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + logger.info('') + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=10), 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['starttime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['starttime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['endtime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['endtime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim7['starttime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim7['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + # second final simple case: insert another claim which precedes (non overlapping/non-touching) all the others + claim8 = {'resource_id': cep4_id, 'starttime': claim1['starttime']-timedelta(minutes=20), 'endtime': claim1['starttime']-timedelta(minutes=10), 'status': 'tentative', 'claim_size': 1} + self.radb.insertResourceClaims(task_id, [claim8], 'foo', 1, 1) + # test usages twice, once to check the usages generated by insert-triggers, and then to check usages generated by rebuild_resource_usages_from_claims + for i in range(2): + if i == 1: + # make sure the usage table is wiped, so asserts fail when rebuild_resource_usages_from_claims is erroneously roll'ed back. + self.radb._executeQuery('TRUNCATE resource_allocation.resource_usage;') + self.radb.rebuild_resource_usages_from_claims(cep4_id, 'tentative') + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(minutes=100), 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim8['starttime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim8['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime']-timedelta(minutes=1), 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['starttime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['starttime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim1['endtime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['starttime'], 'tentative')['usage']) + self.assertEqual( 3, self.radb.get_resource_usage_at_or_before(cep4_id, claim5['endtime'], 'tentative')['usage']) + self.assertEqual( 4, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['starttime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim3['endtime'], 'tentative')['usage']) + self.assertEqual( 2, self.radb.get_resource_usage_at_or_before(cep4_id, claim4['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim2['endtime'], 'tentative')['usage']) + self.assertEqual( 1, self.radb.get_resource_usage_at_or_before(cep4_id, claim7['starttime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim7['endtime'], 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(minutes=1000), 'tentative')['usage']) + + def test_overlapping_claims(self): + # this is a special testcase to prove a bug found at 2017-08-16 + # the bug was that a claim that should fit, does not fit according to the radb claim-methods. + # first, we'll prove that the bug exists (and that this test fails), + # and then, we'll fix the code, (so this test succeeds) + + # for testing purposes let's give CEP4 storage a total size of 100 + cep4_id = 117 + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100, total_capacity=100)) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + + now = datetime.utcnow() + now -= timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to full hour + + #insert one task, and reuse that for multiple overlapping claims + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now+timedelta(hours=1), + 'foo', 'CEP4') + self.assertTrue(result['inserted']) + task_id = result['task_id'] + + task = self.radb.getTask(task_id) + self.assertTrue(task) + self.assertEqual(task_id, task['id']) + + #create two overlapping claims + claims = [ { 'resource_id': cep4_id, + 'starttime': now, + 'endtime': now+timedelta(hours=0.75), + 'status': 'tentative', + 'claim_size': 40 }, + {'resource_id': cep4_id, + 'starttime': now+timedelta(hours=0.25), + 'endtime': now + timedelta(hours=1), + 'status': 'tentative', + 'claim_size': 40} ] + + # insert the claims + claim_ids = self.radb.insertResourceClaims(task_id, claims, 'foo', 1, 1) + self.assertEqual(2, len(claim_ids)) + claims_org = claims + + #get claim using t1_claim_ids, and check if db version is equal to original + claims = self.radb.getResourceClaims(claim_ids=claim_ids) + self.assertEqual(2, len(claims)) + for claim, claim_org in zip(claims, claims_org): + for key, value in claim_org.items(): + if key != 'status': + self.assertEqual(value, claim_org[key]) + + # try to update the claim status to claimed, should succeed. + self.assertTrue(self.radb.updateResourceClaims(claim_ids, status='claimed')) + for claim in self.radb.getResourceClaims(claim_ids=claim_ids): + self.assertEqual('claimed', claim['status']) + + # check the resource usage trend + logger.info("resource usages:\n%s", pformat(self.radb.getResourceUsages(now-timedelta(hours=1.0), now+timedelta(hours=2.0), cep4_id))) + self.assertEqual(0, self.radb.get_max_resource_usage_between(cep4_id, now-timedelta(hours=1.0), now-timedelta(hours=0.01), 'claimed')['usage']) + self.assertEqual(40, self.radb.get_max_resource_usage_between(cep4_id, now+timedelta(hours=0.0), now+timedelta(hours=0.2), 'claimed')['usage']) + self.assertEqual(80, self.radb.get_max_resource_usage_between(cep4_id, now+timedelta(hours=0.3), now+timedelta(hours=0.6), 'claimed')['usage']) + self.assertEqual(40, self.radb.get_max_resource_usage_between(cep4_id, now+timedelta(hours=0.80), now+timedelta(hours=1.0), 'claimed')['usage']) + + #check for a time range encapsulating the full task + self.assertEqual(80, self.radb.get_max_resource_usage_between(cep4_id, now+timedelta(hours=-0.1), now+timedelta(hours=1.1), 'claimed')['usage']) + + #check for a time range not including the task + self.assertEqual(0, self.radb.get_max_resource_usage_between(cep4_id, now+timedelta(hours=1.1), now+timedelta(hours=2.0), 'claimed')['usage']) + self.assertEqual(0, self.radb.get_max_resource_usage_between(cep4_id, now-timedelta(hours=1.1), now-timedelta(hours=1.0), 'claimed')['usage']) + + # check that there are no overlapping conflicting claims/tasks + for claim in claims: + #both claims should overlap with one (the other) claim + self.assertEqual(1, len(self.radb.get_overlapping_claims(claim['id'], 'claimed'))) + self.assertEqual(1, len(self.radb.get_overlapping_tasks(claim['id'], 'claimed'))) + + #and there should be no overlapping claims of other status + self.assertEqual(0, len(self.radb.get_overlapping_claims(claim['id'], 'tentative'))) + self.assertEqual(0, len(self.radb.get_overlapping_tasks(claim['id'], 'tentative'))) + + #check claimable_capacity for various timestamps + self.assertEqual(100, self.radb.get_resource_claimable_capacity(cep4_id, now-timedelta(hours=1.0), now-timedelta(hours=1.0))) + self.assertEqual(60, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=0.0), now+timedelta(hours=0.0))) + self.assertEqual(60, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=0.2), now+timedelta(hours=0.2))) + self.assertEqual(20, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=0.3), now+timedelta(hours=0.3))) + self.assertEqual(20, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=0.5), now+timedelta(hours=0.5))) + self.assertEqual(60, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=0.75), now+timedelta(hours=0.75))) + self.assertEqual(60, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=0.8), now+timedelta(hours=0.8))) + self.assertEqual(100, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=1.0), now+timedelta(hours=1.0))) + self.assertEqual(100, self.radb.get_resource_claimable_capacity(cep4_id, now+timedelta(hours=10.0), now+timedelta(hours=10.0))) + + #check claimable_capacity for full task's timewindow (+extra) + self.assertEqual(20, self.radb.get_resource_claimable_capacity(cep4_id, now-timedelta(hours=10.0), now+timedelta(hours=10.0))) + + + #add an extra claim, overlapping with only the last claim of size 40. So it should fit (100-40=60 and 60>30). + extra_claim = { 'resource_id': cep4_id, + 'starttime': now+timedelta(hours=0.8), + 'endtime': now+timedelta(hours=0.9), + 'status': 'tentative', + 'claim_size': 30 } + extra_claim_ids = self.radb.insertResourceClaims(task_id, [extra_claim], 'foo', 1, 1) + self.assertEqual(1, len(extra_claim_ids)) + + #check the extra_claim's status, should be tentative. (used to be conflict before bug of 2017-08-16) + for claim in self.radb.getResourceClaims(claim_ids=extra_claim_ids): + self.assertEqual('tentative', claim['status']) + + # update the extra_claim status to 'claimed'. Should succeed. + self.assertTrue(self.radb.updateResourceClaims(extra_claim_ids, status='claimed')) #(used to fail before bug of 2017-08-16) + for claim in self.radb.getResourceClaims(claim_ids=extra_claim_ids): + self.assertEqual('claimed', claim['status']) #(used to be conflict before bug of 2017-08-16) + + #and finally, the task should be able to be scheduled as well. + self.assertTrue(self.radb.updateTask(task_id, task_status='scheduled')) + self.assertEqual('scheduled', self.radb.getTask(task_id)['status']) + + def test_reinsert_task(self): + # this is a special testcase to prove a bug found at 2017-08-28 + # the bug was that a specification is re-inserted, which causes the original spec to be deleted... + # ... wich cascades, and deletes the task, and its claims, and it's usages, but that failed on the usages. + # I've tried to reproduce the above bug which we see in production, + # but unfortunately, I cannot reproduce it. The code just works as intended. + # So, after consulting with JDM and AR, we decided to keep this test, and develop a consistency-check-method on the usage table. + # We'll keep this new test anyway, just to prove that these cases work as expected. + + # for testing purposes let's give CEP4 storage a total size of 100 + cep4_id = 117 + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100, total_capacity=100)) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + + now = datetime.utcnow() + now -= timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to full hour + + # insert one task, and reuse that for multiple overlapping claims + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now + timedelta(hours=1), 'first content', + 'CEP4') + self.assertTrue(result['inserted']) + task_id = result['task_id'] + + task = self.radb.getTask(task_id) + self.assertTrue(task) + self.assertEqual(task_id, task['id']) + self.assertEqual('first content', self.radb.getSpecification(task['specification_id'])['content']) + + # prove that we can re-insert the spec/task, and that the new task is indeed inserted and new + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now + timedelta(hours=1), 'second content', + 'CEP4') + self.assertTrue(result['inserted']) + self.assertNotEqual(task_id, result['task_id']) # we should have a new id because it was re-inserted + task_id = result['task_id'] + + task = self.radb.getTask(task_id) + self.assertTrue(task) + self.assertEqual(task_id, task['id']) + self.assertEqual('second content', self.radb.getSpecification(task['specification_id'])['content']) #spec content should have been renewed + + # create and insert a claim + claim = {'resource_id': cep4_id, + 'starttime': task['starttime'], + 'endtime': task['endtime'], + 'status': 'tentative', + 'claim_size': 40} + claim_ids = self.radb.insertResourceClaims(task_id, [claim], 'foo', 1, 1) + self.assertEqual(1, len(claim_ids)) + self.assertEqual(1, len(self.radb.getResourceClaims(claim_ids=claim_ids))) + + #check the extra_claim's status, should be tentative. + for claim in self.radb.getResourceClaims(claim_ids=claim_ids): + self.assertEqual('tentative', claim['status']) + + # update the extra_claim status to 'claimed'. Should succeed. + self.assertTrue(self.radb.updateResourceClaims(claim_ids, status='claimed')) + self.assertEqual(1, len(self.radb.getResourceClaims(claim_ids=claim_ids))) + for claim in self.radb.getResourceClaims(claim_ids=claim_ids): + self.assertEqual('claimed', claim['status']) + + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(hours=0.5), 'claimed')['usage']) + self.assertEqual(40, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=0.5), 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=1.5), 'claimed')['usage']) + + # prove again that we can re-insert the spec/task (now with claims), and that the new task is indeed inserted and new, + # and that the claim(s) and usage(s) were actually deleted (via cascading deletes) + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now + timedelta(hours=1), 'third content', + 'CEP4') + self.assertTrue(result['inserted']) + self.assertNotEqual(task_id, result['task_id']) # we should have a new id because it was re-inserted + task_id = result['task_id'] + + task = self.radb.getTask(task_id) + self.assertTrue(task) + self.assertEqual(task_id, task['id']) + self.assertEqual('third content', self.radb.getSpecification(task['specification_id'])['content']) #spec content should have been renewed + + # this newly inserted spec/task should have no claims anymore + self.assertEqual( 0, len(self.radb.getResourceClaims())) + # and all usages should now be 0 + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(hours=0.5), 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=0.5), 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=1.5), 'claimed')['usage']) + + # 2017-08-29: ok, we could not reproduce the bug found on production, + # so it seems there is a strange corner case which caused the usages table to become inconsistent. + # after consulting with JDM and AR, we decided to mimic this inconsistency by 'corrupting' the table manually in this test, + # and then act on the inconsistent table by detecting the inconsistency, and automatically repairing the usages table. + # so, let's do that in the remainder of this test. + + #insert a claim again (cause we don't have a claim anymore since we inserted the spec/taks above) + claim = {'resource_id': cep4_id, + 'starttime': task['starttime'], + 'endtime': task['endtime'], + 'status': 'tentative', + 'claim_size': 40} + claim_ids = self.radb.insertResourceClaims(task_id, [claim], 'foo', 1, 1) + self.assertEqual(1, len(claim_ids)) + self.assertEqual(1, len(self.radb.getResourceClaims(claim_ids=claim_ids))) + + # usages should be ok + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(hours=0.5), 'tentative')['usage']) + self.assertEqual(40, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=0.5), 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=1.5), 'tentative')['usage']) + + # now let's break the usages table (intentionally, to mimic the production inconsistenty) + # we shift one entry (where the claim starts) by one minute + # result should be that when we change the claim (or delete the claim cause it's task/spec was deleted), + # that the associated usage at the claim's starttime cannot be found anymore (which is the bug on production). + self.radb._executeQuery("UPDATE resource_allocation.resource_usage SET as_of_timestamp = %s WHERE as_of_timestamp = %s;", (now+timedelta(minutes=1), now)) + + # check that the usages were indeed changed (the first one shifted in time) + usages = self.radb.getResourceUsages()[cep4_id]['tentative'] + self.assertEqual({'usage': 40, 'as_of_timestamp': now+timedelta(minutes=1)}, usages[0]) + self.assertEqual({'usage': 0, 'as_of_timestamp': task['endtime']}, usages[1]) + + # and prove again that we can re-insert the spec/task (now with claims and a corrupted usage table), and that the new task is indeed inserted and new, + # and that the claim(s) and usage(s) were actually deleted (via cascading deletes) + # 2017-08-29: YEAH! the insert fails just like on production. Now we can start making a fix! + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', now, now + timedelta(hours=1), 'fourth content', + 'CEP4') + self.assertTrue(result['inserted']) + self.assertNotEqual(task_id, result['task_id']) # we should have a new id because it was re-inserted + task_id = result['task_id'] + + task = self.radb.getTask(task_id) + self.assertTrue(task) + self.assertEqual(task_id, task['id']) + self.assertEqual('fourth content', self.radb.getSpecification(task['specification_id'])['content']) #spec content should have been renewed + + # this newly inserted spec/task should have no claims anymore + self.assertEqual( 0, len(self.radb.getResourceClaims())) + # and all usages should now be 0 + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now-timedelta(hours=0.5), 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=0.5), 'tentative')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, now+timedelta(hours=1.5), 'tentative')['usage']) + + def test_claims_on_partially_misc_filled_resource(self): + # this is a special testcase to prove a bug found at 2017-08-24 + # the bug was that a claim that should fit, does not fit according to the radb claim-methods. + # the reason that it (erroneously) does not fit is an error in the get_resource_claimable_capacity_between method in sql. + # first, we'll prove that the bug exists (and that this test fails), + # and then, we'll fix the code, (so this test succeeds) + + #first make sure that there are no specs/tasks/claims lingering around + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + # for testing purposes let's give CEP4 storage a total size of 100 + cep4_id = 117 + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100, total_capacity=100)) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['available_capacity']) + self.assertEqual( 0, self.radb.getResources(cep4_id, include_availability=True)[0]['used_capacity']) + self.assertEqual( 0, self.radb.getResources(cep4_id, include_availability=True)[0]['misc_used_capacity']) + + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + #insert a task + result = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, start+timedelta(hours=2), 'foo', 'CEP4') + self.assertTrue(result['inserted']) + task_id = result['task_id'] + + task = self.radb.getTask(task_id) + self.assertTrue(task) + self.assertEqual(task_id, task['id']) + + #check if there is indeed still 100 claimable_capacity, and that there is no resource_usage + #because there are no claims yet. + self.assertEqual(100, self.radb.get_resource_claimable_capacity(cep4_id, task['starttime'], task['endtime'])) + self.assertEqual(0, self.radb.get_max_resource_usage_between(cep4_id, task['starttime'], task['endtime'], 'claimed')['usage']) + self.assertEqual(0, self.radb.get_current_resource_usage(cep4_id, 'claimed')['usage']) + + #add a claim for the task which should fit. + #there is 100 available + claim = { 'resource_id': cep4_id, + 'starttime': task['starttime'], + 'endtime': task['endtime'], + 'status': 'tentative', + 'claim_size': 60 } + claim_id = self.radb.insertResourceClaims(task_id, [claim], 'foo', 1, 1)[0] + claim = self.radb.getResourceClaims(claim_ids=[claim_id])[0] + self.assertEqual('tentative', claim['status']) + + # because the claim is still tentative, there should be 100 claimable_capacity left + self.assertEqual(100, self.radb.get_resource_claimable_capacity(cep4_id, task['starttime'], task['endtime'])) + + # and the capacities of the resource should still be the same as in the beginning + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['available_capacity']) + self.assertEqual( 0, self.radb.getResources(cep4_id, include_availability=True)[0]['used_capacity']) + self.assertEqual( 0, self.radb.getResources(cep4_id, include_availability=True)[0]['misc_used_capacity']) + + # set the status to 'claimed', and check it. + self.assertTrue(self.radb.updateResourceClaims(claim_id, status='claimed')) + claim = self.radb.getResourceClaims(claim_ids=[claim_id])[0] + self.assertEqual('claimed', claim['status']) + + #now the resource_usage should be 60 + self.assertEqual(60, self.radb.get_max_resource_usage_between(cep4_id, task['starttime'], task['endtime'], 'claimed')['usage']) + self.assertEqual(60, self.radb.get_current_resource_usage(cep4_id, 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, start-timedelta(hours=1), 'claimed')['usage']) + self.assertEqual(60, self.radb.get_resource_usage_at_or_before(cep4_id, claim['starttime'], 'claimed')['usage']) + self.assertEqual(60, self.radb.get_resource_usage_at_or_before(cep4_id, start+timedelta(hours=0.25), 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, claim['endtime'], 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, task['endtime'], 'claimed')['usage']) + self.assertEqual( 0, self.radb.get_resource_usage_at_or_before(cep4_id, task['endtime']+timedelta(hours=1), 'claimed')['usage']) + + # assume the data has NOT been written YET, and that the claim of size 60 does NOT occupy 60 YET on the resource + # this happens during the observation. Data is being written, but the system does not know it yet. + # then there should be still be 100-nothing=100 claimable_capacity left + # self.assertEqual(100, self.radb.get_resource_claimable_capacity(cep4_id, task['starttime'], task['endtime'])) + + # assume the observation finished, and data has been written + # so, the claim of size 60 now occupies 60 on the resource + # that would be detected (by the storagequeryservice) and propagated into the radb + # so, let's update the available_capacity + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100-60)) + # and there should be 100-60=40 claimable_capacity left + self.assertEqual(40, self.radb.get_resource_claimable_capacity(cep4_id, task['starttime'], task['endtime'])) + + # check the capacities of the resource + # please note that the misc_used_capacity=0 because we used exactly the same amount of diskspace as was claimed (as it should be) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + self.assertEqual( 40, self.radb.getResources(cep4_id, include_availability=True)[0]['available_capacity']) + self.assertEqual( 60, self.radb.getResources(cep4_id, include_availability=True)[0]['used_capacity']) + self.assertEqual( 0, self.radb.getResources(cep4_id, include_availability=True)[0]['misc_used_capacity']) + + # so far, so good... + # now onto the situation in practice.... + # suppose there is some additional (20) miscelaneous data on cep4, which is not known in claims (like backups/logs/other_data) + # this should be reflected in the available_capacity and misc_used_capacity + # available_capacity = 100-60-20 : 60 is claimed and in use, and 20 is other unaccounted for data. + self.assertTrue(self.radb.updateResourceAvailability(cep4_id, available_capacity=100-60-20)) + self.assertEqual(100, self.radb.getResources(cep4_id, include_availability=True)[0]['total_capacity']) + self.assertEqual( 20, self.radb.getResources(cep4_id, include_availability=True)[0]['available_capacity']) + self.assertEqual( 80, self.radb.getResources(cep4_id, include_availability=True)[0]['used_capacity']) + # and the used_capacity of 70 should be build up of the parts: resource_usage=50 and misc_used_capacity=20 + self.assertEqual( 60, self.radb.get_resource_usage_at_or_before(cep4_id, start+timedelta(hours=0.5), 'claimed')['usage']) + self.assertEqual( 20, self.radb.getResources(cep4_id, include_availability=True)[0]['misc_used_capacity']) + # and the resource_usage should still be 50 (cause no claims were added/changed) + self.assertEqual(60, self.radb.get_max_resource_usage_between(cep4_id, task['starttime'], task['endtime'], 'claimed')['usage']) + self.assertEqual(60, self.radb.get_current_resource_usage(cep4_id, 'claimed')['usage']) + # but, there should be less claimable capacity left: 100 -60 (claim) -20 (misc_data) = 20 claimable_capacity left + self.assertEqual(20, self.radb.get_resource_claimable_capacity(cep4_id, task['starttime'], task['endtime'])) + # and for a new task (where there are no claims yet), there should be less claimable capacity left: 100-20 (misc_data) = 80 claimable_capacity left + self.assertEqual(80, self.radb.get_resource_claimable_capacity(cep4_id, task['endtime'] + timedelta(hours=1), task['endtime'] + timedelta(hours=2))) + + #so, it should be possible to add an extra claim of 15 during this task (which should fit!) + claim2 = { 'resource_id': cep4_id, + 'starttime': task['starttime'], + 'endtime': task['endtime'], + 'status': 'tentative', + 'claim_size': 15 } + claim2_id = self.radb.insertResourceClaims(task_id, [claim2], 'foo', 1, 1)[0] + claim2 = self.radb.getResourceClaims(claim_ids=[claim2_id])[0] + self.assertEqual('tentative', claim2['status']) + + # and the claim should be able to have the status set to 'claimed'. check it. + self.assertTrue(self.radb.updateResourceClaims(claim2_id, status='claimed')) + claim2 = self.radb.getResourceClaims(claim_ids=[claim2_id])[0] + self.assertEqual('claimed', claim2['status']) + + #and, it should also be possible to add an extra claim of 75 after this task (where there is no claim yet) (which should fit!) + claim3 = { 'resource_id': cep4_id, + 'starttime': task['endtime'] + timedelta(hours=1), + 'endtime': task['endtime'] + timedelta(hours=2), + 'status': 'tentative', + 'claim_size': 75 } + claim3_id = self.radb.insertResourceClaims(task_id, [claim3], 'foo', 1, 1)[0] + claim3 = self.radb.getResourceClaims(claim_ids=[claim3_id])[0] + self.assertEqual('tentative', claim3['status']) + + # and the claim should be able to have the status set to 'claimed'. check it. + self.assertTrue(self.radb.updateResourceClaims(claim3_id, status='claimed')) + claim3 = self.radb.getResourceClaims(claim_ids=[claim3_id])[0] + self.assertEqual('claimed', claim3['status']) + + def test_double_claim_should_result_in_conflict_overlap_in_future(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start + timedelta(minutes=5), + start + timedelta(hours=2, minutes=5), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('conflict', self.radb.getTask(task2_id)['status']) + + def test_double_claim_should_result_in_conflict_within_larger_claim(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start + timedelta(minutes=5), + start + timedelta(hours=1, minutes=50), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('conflict', self.radb.getTask(task2_id)['status']) + + def test_double_claim_should_result_in_conflict_overlap_in_the_past(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start + timedelta(minutes=-5), + start + timedelta(hours=1, minutes=55), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('conflict', self.radb.getTask(task2_id)['status']) + + def test_double_claim_should_result_in_conflict_overlap_in_the_past_and_future(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start + timedelta(minutes=-5), + start + timedelta(hours=2, minutes=5), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('conflict', self.radb.getTask(task2_id)['status']) + + def test_double_claim_should_result_in_conflict_overlap_exactly(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('conflict', self.radb.getTask(task2_id)['status']) + + def test_double_claim_should_result_in_approved_with_no_overlap_future(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start + timedelta(hours=3), + start + timedelta(hours=5), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('approved', self.radb.getTask(task2_id)['status']) + + def test_double_claim_should_result_in_approved_with_no_overlap_past(self): + now = datetime.utcnow() + start = now - timedelta(minutes=now.minute, seconds=now.second, microseconds=now.microsecond) # round to current full hour + + for spec in self.radb.getSpecifications(): + self.radb.deleteSpecification(spec['id']) # cascades into tasks and claims + + result1 = self.radb.insertSpecificationAndTask(0, 0, 'approved', 'observation', start + timedelta(hours=3), + start + timedelta(hours=5), 'foo', 'CEP4') + task1_id = result1['task_id'] + + task1 = self.radb.getTask(task1_id) + + claim1 = { 'resource_id': 212, + 'starttime': task1['starttime'], + 'endtime': task1['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim1_id = self.radb.insertResourceClaims(task1_id, [claim1], 'foo', 1, 1)[0] + self.radb.updateResourceClaims(claim1_id, status='claimed') + + # claim same + + result2 = self.radb.insertSpecificationAndTask(1, 1, 'approved', 'observation', start, + start + timedelta(hours=2), 'foo', 'CEP4') + task2_id = result2['task_id'] + + task2 = self.radb.getTask(task2_id) + + claim2 = { 'resource_id': 212, + 'starttime': task2['starttime'], + 'endtime': task2['endtime'], + 'status': 'tentative', + 'claim_size': 96 } + claim2_id = self.radb.insertResourceClaims(task2_id, [claim2], 'foo', 1, 1)[0] + + self.radb.updateResourceClaims(claim2_id, status='claimed') + + self.assertEqual('approved', self.radb.getTask(task2_id)['status']) if __name__ == "__main__": diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini b/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini index a4bb85e8ea9ddd771739132be83ed5157fe51264..0095ddcd2b3a0db93aec6a0763a1778e7722f060 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/bin/raewebservice.ini @@ -1,5 +1,5 @@ [program:raewebservice] -command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec raewebservice -p 7412' +command=/bin/bash -c 'source $LOFARROOT/lofarinit.sh;exec raewebservice' user=lofarsys stopsignal=INT ; KeyboardInterrupt stopasgroup=true ; bash does not propagate signals diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py index 5fbe368f15787ffa196a0586e04f400ae61a78ca..cbeca9a749b1278b110e9f1f5d9fcff164756623 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/changeshandler.py @@ -127,7 +127,7 @@ class ChangesHandler: '''_handleChange appends a change in the changes list and calls the onChangedCallback. :param change: dictionary with the change''' with self._lock: - change['timestamp'] = datetime.utcnow().isoformat() + change['timestamp'] = datetime.utcnow() self._changeNumber += 1 change['changeNumber'] = self._changeNumber self._changes.append(change) @@ -254,9 +254,6 @@ class ChangesHandler: self._handleChange(change) def getEventsSince(self, since_timestamp): - if isinstance(since_timestamp, datetime): - since_timestamp = since_timestamp.isoformat() - with self._lock: return [x for x in self._changes if x['changeType'] == CHANGE_EVENT_TYPE and x['timestamp'] >= since_timestamp] @@ -267,12 +264,6 @@ class ChangesHandler: return -1L def clearChangesBefore(self, min_timestamp_for_changes, min_timestamp_for_logevents): - if isinstance(min_timestamp_for_changes, datetime): - min_timestamp_for_changes = min_timestamp_for_changes.isoformat() - - if isinstance(min_timestamp_for_logevents, datetime): - min_timestamp_for_logevents = min_timestamp_for_logevents.isoformat() - with self._lock: self._changes = [x for x in self._changes if ((x['changeType'] == CHANGE_EVENT_TYPE and x['timestamp'] >= min_timestamp_for_logevents) or diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py index 6134a9152f2a6663b2f494b2115d2aa21b9615a7..b59b5ef705b9a1e75d4373880153fe9bfcb3606a 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/mom.py @@ -71,6 +71,9 @@ def updateTaskMomDetails(task, momrpc): t['mom_object_group_mom2object_id'] = m.get('object_group_mom2objectid') t['mom_object_parent_group_id'] = m['parent_group_mom2id'] t['mom_object_parent_group_name'] = m['parent_group_name'] + elif t['type'] == 'reservation': + t['project_name'] = 'Reservations' + t['project_mom_id'] = -97 else: t['project_name'] = 'OTDB Only' t['project_mom_id'] = -98 diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js index de53c8935453539e956a1c2ae562ee44f877b138..443eca9eaa32ca8425c95577a4c1b35e90d815e7 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/chartresourceusagecontroller.js @@ -86,6 +86,17 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc } var selected_resource_id = $scope.dataService.selected_resource_id; + if(selected_resource_id === undefined) { + //try to select first storage resource as default selected_resource_id + var storageResources = $scope.dataService.resources.filter(function(r) { return r.type_name == 'storage'; }); + if(storageResources.length > 0) { + $scope.dataService.selected_resource_id = storageResources[0].id; + } else { + //else, just the first resource + $scope.dataService.selected_resource_id = self.resources[0].id; + } + } + if(selected_resource_id === undefined) return; @@ -168,7 +179,16 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc value = usage.usage; } } - usage_data.push([timestamp.getTime(), value]); + if(usage_data.length > 0) { + //overwrite last value if timestamps are equal, otherwise append + if(usage_data[usage_data.length-1][0] == timestamp.getTime()) { + usage_data[usage_data.length-1][1] = value; + } else { + usage_data.push([timestamp.getTime(), value]); + } + } else { + usage_data.push([timestamp.getTime(), value]); + } t_idx += 1; } @@ -246,7 +266,6 @@ chartResourceUsageControllerMod.controller('ChartResourceUsageController', ['$sc } }; - $scope.$watch('dataService.selected_resource_id', updateChartDataAsync); $scope.$watch('dataService.resourceUsagesForSelectedResource', updateChartDataAsync); $scope.$watch('dataService.viewTimeSpan', updateChartDataAsync, true); $scope.$watch('$parent.enabled', function() { setTimeout(updateChartDataAsync, 500); } ); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js index 10baa4d077e98382f155673f5494d8a295ab949f..e973db2bd649e778de1b06d5b526ebe0ad28e213 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/cleanupcontroller.js @@ -135,8 +135,8 @@ cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$m }); }; - function deleteTaskData(task, delete_is, delete_cs, delete_uv, delete_im, delete_img, delete_pulp, delete_scratch) { - var params = {delete_is:delete_is, delete_cs:delete_cs, delete_uv:delete_uv, delete_im:delete_im, delete_img:delete_img, delete_pulp:delete_pulp, delete_scratch:delete_scratch}; + function deleteTaskData(task, delete_is, delete_cs, delete_uv, delete_im, delete_img, delete_pulp, delete_scratch, force_delete=false) { + var params = {delete_is:delete_is, delete_cs:delete_cs, delete_uv:delete_uv, delete_im:delete_im, delete_img:delete_img, delete_pulp:delete_pulp, delete_scratch:delete_scratch, force_delete:force_delete}; $http.delete('/rest/tasks/' + task.id + '/cleanup', {data: params}).error(function(result) { console.log("Error. Could cleanup data for task " + task.id + ", " + result); }).success(function(result) { @@ -163,6 +163,8 @@ cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$m <label ng-if="has_scratch" style="margin-left:24px">scratch: {{amount_scratch}}<input style="margin-left:8px" type="checkbox" ng-model="$parent.delete_scratch"></label>\ </div>\ <div class="modal-footer">\ + <label style="margin-right:24px" title="force delete action, even if data is (partially) un-ingested or needed by successor pipeline(s)">force\ + <input style="margin-right:2px; margin-top:2px; float: left" type="checkbox" ng-model="$parent.force_delete"></label>\ <button class="btn btn-primary" type="button" ng-click="ok()">OK</button>\ <button class="btn btn-warning" type="button" autofocus ng-click="cancel()">Cancel</button>\ </div>', @@ -239,7 +241,7 @@ cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$m $uibModalInstance.close(); for(var du_result of du_results) { var task = du_result.task; - deleteTaskData(task, $scope.delete_is, $scope.delete_cs, $scope.delete_uv, $scope.delete_im, $scope.delete_img, $scope.delete_pulp, $scope.delete_scratch); + deleteTaskData(task, $scope.delete_is, $scope.delete_cs, $scope.delete_uv, $scope.delete_im, $scope.delete_img, $scope.delete_pulp, $scope.delete_scratch, $scope.force_delete); } }; @@ -380,14 +382,6 @@ cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$m loading: false } - $scope.totalDiskUsageChartSeries = []; - - var cep4storage_resource = dataService.resources.find(function(r) { return r.name == 'cep4storage'; }); - if(cep4storage_resource) { - $scope.totalDiskUsageChartSeries = [{name:'Free', data:[100.0*cep4storage_resource.available_capacity/cep4storage_resource.total_capacity], color:'#a3f75c'}, - {name:'Used', data:[100.0*cep4storage_resource.used_capacity/cep4storage_resource.total_capacity], color:'#f45b5b'}]; - } - $scope.totalDiskUsageChartConfig = { options: { chart: { @@ -435,7 +429,7 @@ cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$m pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>' }, }, - series: $scope.totalDiskUsageChartSeries, + series: [], title: { text: 'CEP4 total disk usage' }, @@ -445,6 +439,27 @@ cleanupControllerMod.controller('CleanupController', ['$scope', '$uibModal', '$m loading: false } + var cep4storage_resource = dataService.resources.find(function(r) { return r.name == 'CEP4_storage:/data'; }); + if(cep4storage_resource) { + dataService.getProjectsDiskUsage().then(function(result) { + if(result.found) { + var projects_du = result.projectdir.disk_usage; + var misc_du = cep4storage_resource.used_capacity - projects_du; + var total_cap = cep4storage_resource.total_capacity; + + $scope.totalDiskUsageChartConfig.series = [{name:'Free ' + dataService.humanreadablesize(cep4storage_resource.available_capacity, 1) + 'B', + data:[100.0*cep4storage_resource.available_capacity/total_cap], + color:'#66ff66'}, + {name:'Misc ' + dataService.humanreadablesize(misc_du, 1) + 'B', + data:[100.0*misc_du/total_cap], + color:'#aaaaff'}, + {name:'Projects ' + dataService.humanreadablesize(projects_du, 1) + 'B', + data:[100.0*projects_du/total_cap], + color:'#ff6666'}]; + } + }); + } + var loadTaskDiskUsage = function(otdb_id, force) { $scope.current_otdb_id = otdb_id; $scope.current_project_name = undefined; diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js index c4f6bbc54d0f4ae2e9c2870a568edc805254c9a3..88fee8beb924f412664c11aff40efb4e27068ae6 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js @@ -25,6 +25,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.resourcesWithClaims = []; self.filteredTasks = []; + self.filteredTasksDict = {}; self.taskTimes = {}; self.resourceClaimTimes = {}; @@ -60,15 +61,15 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, //loadResourceClaims is enabled when any controller using resourceclaims is enabled self.loadResourceClaims = false; - self.humanreadablesize = function(num) { + self.humanreadablesize = function(num, num_digits=3) { var units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']; for(unit of units) { if(Math.abs(num) < 1000.0) { - return num.toPrecision(4).toString() + unit; + return num.toFixed(num_digits).toString() + unit; } num /= 1000.0; } - return num.toPrecision(5).toString() + 'Y'; + return num.toFixed(num_digits).toString() + 'Y'; } self.isTaskIdSelected = function(task_id) { @@ -222,28 +223,31 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, //only keep the currently loading chunks self.loadingChunksQueue = self.loadingChunksQueue.filter(function(c) { return c.loading; }); - var chunkFactor = self.projectMode ? 7 : 1; + var chunkFactor = self.projectMode ? 4 : 1; var hourInmsec = 3600000; - var dayInmsec = 24*hourInmsec; + var quartDayInmsec = 6*hourInmsec; for (var timestamp = lowerTS; timestamp < upperTS; ) { if(self.loadedHours.hasOwnProperty(timestamp)) { timestamp += hourInmsec; } else { - var chuckUpperLimit = Math.min(upperTS, timestamp + chunkFactor*dayInmsec); + var chuckUpperLimit = Math.min(upperTS, timestamp + chunkFactor*quartDayInmsec); for (var chunkTimestamp = timestamp; chunkTimestamp < chuckUpperLimit; chunkTimestamp += hourInmsec) { if(self.loadedHours.hasOwnProperty(chunkTimestamp)) break; - - self.loadedHours[chunkTimestamp] = true; } var hourLower = new Date(timestamp); var hourUpper = new Date(chunkTimestamp); if(hourUpper > hourLower) { var chunk = { lower: hourLower, upper: hourUpper, loaded: false, loading: false }; - self.loadingChunksQueue.push(chunk); + if(hourLower < self.lofarTime) { + //prepend at beginning of queue, so we load most recent data first + self.loadingChunksQueue.unshift(chunk); + } else { + self.loadingChunksQueue.push(chunk); + } } timestamp = chunkTimestamp; } @@ -255,6 +259,11 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, for(var i = 0; i < Math.min(3, self.loadingChunksQueue.length); i++) { setTimeout(self.loadNextChunk, i*250); } + + //load the usages as well, if needed. + if (self.loadResourceClaims) { + self.getUsagesForSelectedResource(); + } }; self.loadNextChunk = function() { @@ -282,6 +291,10 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.nrOfLoadingChunks -= 1; self.nrOfLoadedChunks += 1; + for (var timestamp = chunk.lower.getTime(); timestamp < chunk.upper.getTime(); timestamp += 3600000) { + self.loadedHours[timestamp] = true; + } + self.loadNextChunk(); }); } else { @@ -629,18 +642,12 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getResources = function() { var defer = $q.defer(); $http.get('/rest/resources').success(function(result) { - self.resources = result.resources; + //at this moment, we have way too many resources to show in a gantt-tree. + //this make the webscheduler way too slow. + //so, only show the relevant resources, 116 and 117 which are CEP4 bandwith and storage. + self.resources = result.resources.filter(function(r) { return r.id==116 || r.id==117;}); self.resourceDict = self.toIdBasedDict(self.resources); - //try to select first storage resource as default selected_resource_id - var storageResources = self.resources.filter(function(r) { return r.type_name == 'storage'; }); - if(storageResources.length > 0) { - self.selected_resource_id = storageResources[0].id; - } else { - //else, just the first resource - self.selected_resource_id = self.resources[0].id; - } - defer.resolve(); }); @@ -759,7 +766,10 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.getResourceGroups = function() { var defer = $q.defer(); $http.get('/rest/resourcegroups').success(function(result) { - self.resourceGroups = result.resourcegroups; + //at this moment, we have way too many resources to show in a gantt-tree. + //this make the webscheduler way too slow. + //so, only show the relevant resource groups, 1, which is the CEP4 group. + self.resourceGroups = result.resourcegroups.filter(function(r) { return r.id==1;}); self.resourceGroupsDict = self.toIdBasedDict(self.resourceGroups); defer.resolve(); @@ -1331,7 +1341,10 @@ dataControllerMod.controller('DataController', }); }, true); - $scope.$watch('dataService.filteredTaskChangeCntr', dataService.computeMinMaxTaskTimes); + $scope.$watch('dataService.filteredTaskChangeCntr', function() { + dataService.computeMinMaxTaskTimes(); + dataService.filteredTasksDict = dataService.toIdBasedDict(dataService.filteredTasks); + }); $scope.$watch('dataService.lofarTime', function() { if(dataService.autoFollowNow && (Math.round(dataService.lofarTime.getTime()/1000))%5==0) { @@ -1377,13 +1390,6 @@ dataControllerMod.controller('DataController', } }); - $scope.$watch('dataService.claimChangeCntr', function() { - $scope.$evalAsync(function() { - $scope.dataService.getUsagesForSelectedResource(); - $scope.dataService.getResource($scope.dataService.selected_resource_id); - }); - }); - $scope.$watch('dataService.selected_resource_id', function() { $scope.$evalAsync(function() { $scope.dataService.getUsagesForSelectedResource(); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js index 4ebca480cf38a38e819ff72e7901e846d9dde4c5..debb2c8afb8ef9d67c572f1c3183a45b1aaeacdf 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js @@ -161,7 +161,7 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS //start with aggregating all tasks per type, //and plot these in the upper rows, //so we can see the observartion and pipeline scheduling usage/efficiency - for(var type of ['observation', 'pipeline']) { + for(var type of ['observation', 'pipeline', 'reservation']) { var typeTasks = tasks.filter(function(t) { return t.type == type;}).sort(function(a, b) { return a.starttime.getTime() - b.starttime.getTime(); });; var numTypeTasks = typeTasks.length; @@ -265,6 +265,10 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS tasks: [] }; + if(task.type == 'reservation' && project.name.toLowerCase().includes('reservation')) { + availableRow.name = project.name; + } + ganttProjectTypeRows.push(availableRow); ganttRows.push(availableRow); } diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js index 6df74997511eed35e938da801069f054ce7281fe..37006404dcb1c7441c09ff1df3eb99d629afd3de 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js @@ -153,7 +153,7 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat var resourceGroupMemberships = $scope.dataService.resourceGroupMemberships; - var taskDict = $scope.dataService.taskDict; + var tasksDict = $scope.dataService.filteredTasksDict; var numTasks = $scope.dataService.filteredTasks.length; var resourceClaimDict = $scope.dataService.resourceClaimDict; @@ -316,7 +316,7 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat //and finally assign each resourceclaim to its resource in each group for(var claim of resourceClaims) { var resourceId = claim.resource_id; - var task = taskDict[claim.task_id]; + var task = tasksDict[claim.task_id]; if(!task) { continue; @@ -368,24 +368,26 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat if(claims) { for(var claim of claims) { var taskId = claim.task_id; - var task = taskDict[taskId]; - if(taskId in aggregatedClaims) { - if(claim.starttime < aggregatedClaims[taskId].starttime) { - aggregatedClaims[taskId].starttime = claim.starttime.getTime() > task.starttime.getTime() ? claim.starttime : task.starttime; - } - if(claim.endtime > aggregatedClaims[taskId].endtime) { - aggregatedClaims[taskId].endtime = claim.endtime.getTime() < task.endtime.getTime() ? claim.endtime: task.endtime; - } - if(claim.status == 'conflict') { - aggregatedClaims[taskId].status = 'conflict'; - } else if(claim.status != aggregatedClaims[taskId].status && aggregatedClaims[taskId].status != 'conflict') { - aggregatedClaims[taskId].status = 'mixed'; + var task = tasksDict[taskId]; + if(task) { + if(taskId in aggregatedClaims) { + if(claim.starttime < aggregatedClaims[taskId].starttime) { + aggregatedClaims[taskId].starttime = claim.starttime.getTime() > task.starttime.getTime() ? claim.starttime : task.starttime; + } + if(claim.endtime > aggregatedClaims[taskId].endtime) { + aggregatedClaims[taskId].endtime = claim.endtime.getTime() < task.endtime.getTime() ? claim.endtime: task.endtime; + } + if(claim.status == 'conflict') { + aggregatedClaims[taskId].status = 'conflict'; + } else if(claim.status != aggregatedClaims[taskId].status && aggregatedClaims[taskId].status != 'conflict') { + aggregatedClaims[taskId].status = 'mixed'; + } + } else { + aggregatedClaims[taskId] = { starttime: claim.starttime, + endtime: claim.endtime, + status: claim.status + }; } - } else { - aggregatedClaims[taskId] = { starttime: claim.starttime, - endtime: claim.endtime, - status: claim.status - }; } } } @@ -424,7 +426,7 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat for(var ganttRow of ganttRows) { for(var taskId in aggregatedClaims) { var aggClaimForTask = aggregatedClaims[taskId]; - var task = taskDict[taskId]; + var task = tasksDict[taskId]; if(task) { var claimTask = { id: 'aggregatedClaimForTask_' + taskId + '_' + ganttRow.id, diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js index 22ef3636b5adbb06827ee1f62cb8f76ee5877410..e0c916cbdfd29ee745e1e7295950f20e40c567e5 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js @@ -230,7 +230,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic { field: 'cluster', displayName: 'Cluster', enableCellEdit: false, - width: '65', + width: '75', filter: { condition: uiGridConstants.filter.EXACT, type: uiGridConstants.filter.SELECT, @@ -495,7 +495,7 @@ gridControllerMod.controller('GridController', ['$scope', '$window', 'dataServic fillTypeColumFilterSelectOptions(); fillProjectsColumFilterSelectOptions(); fillGroupsColumFilterSelectOptions(); - fillColumFilterSelectOptions(['CEP2', 'CEP4'], $scope.columns.find(function(c) {return c.field == 'cluster'; })); + fillColumFilterSelectOptions(['CEP2', 'CEP4', 'DRAGNET'], $scope.columns.find(function(c) {return c.field == 'cluster'; })); } }; @@ -765,6 +765,27 @@ gridControllerMod.directive('contextMenu', ['$document', '$window', function($do }); } + var selected_post_approved_observations = selected_tasks.filter(function(t) { return t.type == +'observation' && t.status != 'approved'; }); + + if(selected_post_approved_observations.length > 0 && dataService.config.sky_view_base_url) { + var liElement = angular.element('<li><a href="#">Sky view</a></li>'); + ulElement.append(liElement); + liElement.on('click', function() { + closeContextMenu(); + + var window_cntr = 0; + for(var obs of selected_post_approved_observations) { + var url = dataService.config.sky_view_base_url + '/' + obs.otdb_id; + url = row.grid.appScope.sanitize_url(url); + setTimeout(function(url_arg) { + $window.open(url_arg, '_blank'); + }, window_cntr*750, url); + window_cntr += 1; + } + }); + } + var ingest_tasks = selected_tasks.filter(function(t) { return t.ingest_status != undefined; }); if(ingest_tasks.length > 0 && dataService.config.lta_base_url) { @@ -851,36 +872,36 @@ gridControllerMod.directive('contextMenu', ['$document', '$window', function($do }); } - var approved_selected_cep4_tasks = selected_cep4_tasks.filter(function(t) { return t.status == 'approved'; }); + var schedulable_tasks = selected_tasks.filter(function(t) { return t.status == 'approved' || t.status == 'conflict' || t.status == 'error'; }); - if(approved_selected_cep4_tasks.length > 0) { - var liContent = '<li><a href="#">Schedule approved CEP4 task(s)</a></li>' + if(schedulable_tasks.length > 0) { + var liContent = '<li><a href="#">(Re)schedule CEP4 task(s)</a></li>' var liElement = angular.element(liContent); ulElement.append(liElement); liElement.on('click', function() { closeContextMenu(); - for(var pl of approved_selected_cep4_tasks) { + for(var pl of schedulable_tasks) { var newTask = { id: pl.id, status: 'prescheduled' }; dataService.putTask(newTask); } }); } - var scheduled_selected_cep4_tasks = selected_cep4_tasks.filter(function(t) { return (t.status == 'prescheduled' || t.status == 'scheduled' || t.status == 'queued') }); + var unschedulable_selected_tasks = selected_tasks.filter(function(t) { return t.status == 'prescheduled' || t.status == 'scheduled' || t.status == 'queued' || t.status == 'error' || t.status == 'conflict'; }); - if(scheduled_selected_cep4_tasks.length > 0) { - var liContent = '<li><a href="#">Unschedule (pre)scheduled/queued CEP4 tasks</a></li>' + if(unschedulable_selected_tasks.length > 0) { + var liContent = '<li><a href="#">Unschedule (pre)scheduled/queued/error/conflict tasks</a></li>' var liElement = angular.element(liContent); ulElement.append(liElement); liElement.on('click', function() { closeContextMenu(); - for(var t of scheduled_selected_cep4_tasks) { - if(t.status == 'queued') { - var newTask = { id: t.id, status: 'aborted' }; + for(var pl of unschedulable_selected_tasks) { + if(pl.status == 'queued') { + var newTask = { id: pl.id, status: 'aborted' }; dataService.putTask(newTask); } - var newTask = { id: t.id, status: 'approved' }; + var newTask = { id: pl.id, status: 'approved' }; dataService.putTask(newTask); } }); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css index 5d3d7dd21d56feeef41530ff4569622a4edff7f0..93140b295decac918922f81e71ff77e969c5f3b9 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/css/main.css @@ -227,6 +227,10 @@ div.gantt-task.task-status-blocked div{ background-color: #ccffcc !important; } +.grid-cluster-DRAGNET { + background-color: #ffffcc !important; +} + diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html index a49f06ea37fd4b0fa4af736512a54aa6c35248f9..e984e7f3e4fbe60cad50edfdc16d8d5cc1013877 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/templates/index.html @@ -116,7 +116,7 @@ </uib-progress> <div style="float:left; width:100%; top:70px ;" ui-layout options="{flow: 'column'}"> - <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" ui-layout-init-min-width="1160px"> + <div ng-controller="GridController as gridCtrl" style="margin-right: 4px;" ui-layout-init-min-width="1175px"> <div id="grid" ui-grid="gridOptions" ui-grid-edit ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-auto-resize diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py index 4060cf13096b16d0b1ca5378a395e3267718ab39..461f8860c5130c9854abff54e09ac904a6dd4bf0 100755 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/webservice.py @@ -189,7 +189,8 @@ def projects(): def config(): config = {'mom_base_url':'', 'lta_base_url':'', - 'inspection_plots_base_url':'https://proxy.lofar.eu/inspect/HTML/'} + 'inspection_plots_base_url':'https://proxy.lofar.eu/inspect/HTML/', + 'sky_view_base_url':'http://dop344.astron.nl:5000/uvis/id'} if isProductionEnvironment(): config['mom_base_url'] = 'https://lofar.astron.nl/mom3' @@ -214,6 +215,32 @@ def resource(resource_id): return jsonify(result[0]) return jsonify({}) +@app.route('/rest/resources/<int:resource_id>/resourceclaims') +@gzipped +def resourceclaimsForResource(resource_id): + return resourceclaimsForResourceFromUntil(resource_id, None, None) + +@app.route('/rest/resources/<int:resource_id>/resourceclaims/<string:fromTimestamp>') +@gzipped +def resourceclaimsForResourceFrom(resource_id, fromTimestamp=None): + return resourceclaimsForResourceFromUntil(resource_id, fromTimestamp, None) + +@app.route('/rest/resources/<int:resource_id>/resourceclaims/<string:fromTimestamp>/<string:untilTimestamp>') +@gzipped +def resourceclaimsForResourceFromUntil(resource_id, fromTimestamp=None, untilTimestamp=None): + if fromTimestamp and isinstance(fromTimestamp, basestring): + fromTimestamp = asDatetime(fromTimestamp) + + if untilTimestamp and isinstance(untilTimestamp, basestring): + untilTimestamp = asDatetime(untilTimestamp) + + claims = radb().getResourceClaims(lower_bound=fromTimestamp, + upper_bound=untilTimestamp, + resource_ids=[resource_id], + extended=False, + include_properties=True) + return jsonify({'resourceclaims': claims}) + @app.route('/rest/resourcegroups') @gzipped def resourcegroups(): @@ -312,12 +339,41 @@ def getTasksFromUntil(fromTimestamp=None, untilTimestamp=None): untilTimestamp = asDatetime(untilTimestamp) tasks = radb().getTasks(fromTimestamp, untilTimestamp) - - updateTaskMomDetails(tasks, momqueryrpc) - updateTaskStorageDetails(tasks, sqrpc, curpc) + updateTaskDetails(tasks) return jsonify({'tasks': tasks}) +def updateTaskDetails(tasks): + #update the mom details and the storage details in parallel + t1 = Thread(target=updateTaskMomDetails, args=(tasks, momqueryrpc)) + t2 = Thread(target=updateTaskStorageDetails, args=(tasks, sqrpc, curpc)) + t1.daemon = True + t2.daemon = True + t1.start() + t2.start() + + #wait for mom details thread to finish + t1.join() + + #task details (such as name/description) from MoM are done + #get extra details on reserved resources for reservations (while the storage details still run in t2) + reservationTasks = [t for t in tasks if t['type'] == 'reservation'] + if reservationTasks: + reservationClaims = radb().getResourceClaims(task_ids=[t['id'] for t in reservationTasks], extended=True, include_properties=False) + task2claims = {} + for claim in reservationClaims: + if claim['task_id'] not in task2claims: + task2claims[claim['task_id']] = [] + task2claims[claim['task_id']].append(claim) + for task in reservationTasks: + claims = task2claims.get(task['id'], []) + task['name'] = ', '.join(c['resource_name'] for c in claims) + task['description'] = 'Reservation on ' + task['name'] + + #wait for storage details thread to finish + t2.join() + + @app.route('/rest/tasks/<int:task_id>', methods=['GET']) @gzipped def getTask(task_id): @@ -328,8 +384,7 @@ def getTask(task_id): abort(404) task['name'] = 'Task %d' % task['id'] - updateTaskMomDetails(task, momqueryrpc) - updateTaskStorageDetails(task, sqrpc, curpc) + updateTaskDetails([task]) return jsonify({'task': task}) except Exception as e: abort(404) @@ -346,8 +401,7 @@ def getTaskByOTDBId(otdb_id): abort(404) task['name'] = 'Task %d' % task['id'] - updateTaskMomDetails(task, momqueryrpc) - updateTaskStorageDetails(task, sqrpc, curpc) + updateTaskDetails([task]) return jsonify({'task': task}) except Exception as e: abort(404) @@ -364,8 +418,7 @@ def getTaskByMoMId(mom_id): abort(404) task['name'] = 'Task %d' % task['id'] - updateTaskMomDetails(task, momqueryrpc) - updateTaskStorageDetails(task, sqrpc, curpc) + updateTaskDetails([task]) return jsonify({'task': task}) except Exception as e: abort(404) @@ -378,10 +431,7 @@ def getTasksByMoMGroupId(mom_group_id): try: mom_ids = momqueryrpc.getTaskIdsInGroup(mom_group_id)[str(mom_group_id)] tasks = radb().getTasks(mom_ids=mom_ids) - - updateTaskMomDetails(tasks, momqueryrpc) - updateTaskStorageDetails(tasks, sqrpc, curpc) - + updateTaskDetails(tasks) return jsonify({'tasks': tasks}) except Exception as e: abort(404) @@ -392,10 +442,7 @@ def getTasksByMoMParentGroupId(mom_parent_group_id): try: mom_ids = momqueryrpc.getTaskIdsInParentGroup(mom_parent_group_id)[str(mom_parent_group_id)] tasks = radb().getTasks(mom_ids=mom_ids) - - updateTaskMomDetails(tasks, momqueryrpc) - updateTaskStorageDetails(tasks, sqrpc, curpc) - + updateTaskDetails(tasks) return jsonify({'tasks': tasks}) except Exception as e: abort(404) @@ -406,49 +453,72 @@ def putTask(task_id): request.headers['Content-Type'].startswith('application/json'): try: updatedTask = json_loads(request.data) - logger.info('putTask: updatedTask: %s', updatedTask) if task_id != int(updatedTask['id']): abort(404, 'task_id in url is not equal to id in request.data') - org_task = radb().getTask(task_id) - - if not org_task: - abort(404, "unknown task %s" % updatedTask) - - for timeprop in ['starttime', 'endtime']: - if timeprop in updatedTask: - try: - updatedTask[timeprop] = asDatetime(updatedTask[timeprop]) - except ValueError: - abort(400, 'timestamp not in iso format: ' + updatedTask[timeprop]) + #check if task is known + task = radb().getTask(task_id) + if not task: + abort(404, "unknown task %s" % str(updatedTask)) + # first handle start- endtimes... if 'starttime' in updatedTask or 'endtime' in updatedTask: - # block time edits in productin for now until we've replaced the old scheduler. + logger.info('starttime or endtime in updatedTask: %s', updatedTask) if isProductionEnvironment(): - abort(403, 'Editing of startime/endtime of tasks by users is not yet approved') + abort(403, 'Editing of %s of tasks by users is not yet approved' % (time,)) + + #update dict for otdb spec + spec_update = {} + + for timeprop in ['starttime', 'endtime']: + if timeprop in updatedTask: + try: + updatedTask[timeprop] = asDatetime(updatedTask[timeprop]) + except ValueError: + abort(400, 'timestamp not in iso format: ' + updatedTask[timeprop]) + otdb_key = 'LOFAR.ObsSW.Observation.' + ('startTime' if timeprop == 'starttime' else 'stopTime') + spec_update[otdb_key] = updatedTask[timeprop].strftime('%Y-%m-%d %H:%M:%S') + + #update timestamps in both otdb and radb + otdbrpc.taskSetSpecification(task['otdb_id'], spec_update) # update the task's (and its claims) start/endtime # do not update the tasks status directly via the radb. See few lines below. task status is routed via otdb (and then ends up in radb automatically) # it might be that editing the start/end time results in a (rabd)task status update (for example to 'conflict' due to conflicting claims) # that's ok, since we'll update the status to the requested status later via otdb (see few lines below) - radb().updateTaskAndResourceClaims(task_id, starttime=updatedTask.get('starttime'), endtime=updatedTask.get('endtime')) + radb().updateTaskAndResourceClaims(task_id, + starttime=updatedTask.get('starttime'), + endtime=updatedTask.get('endtime')) + # ...then, handle status update which might trigger resource assignment, + # for which the above updated times are needed if 'status' in updatedTask: - if isProductionEnvironment() and org_task['type'] == 'observation': - abort(403, 'Editing of observation status by users is not yet approved') - try: - otdbrpc.taskSetStatus(org_task['otdb_id'], updatedTask['status']) + #update status in otdb only + #the status change will propagate automatically into radb via other services (by design) + otdbrpc.taskSetStatus(task['otdb_id'], updatedTask['status']) + + #we expect the status in otdb/radb to eventually become what we asked for... + expected_statuses = set([updatedTask['status']]) - #block until radb and mom task status are equal to otdb task status (with timeout) + #except for the prescheduled status, because then the resource assigner tries + #to schedule the task, and it will end up in either 'scheduled', 'conflict', 'error' state. + if updatedTask['status'] == 'prescheduled': + expected_statuses = set(['scheduled', 'conflict', 'error']) + + #block until radb and mom task status are equal to the expected_statuses (with timeout) start_wait = datetime.utcnow() while True: - org_task = radb().getTask(task_id) - details = momqueryrpc.getObjectDetails(org_task['mom_id']).get(org_task['mom_id']) + task = radb().getTask(otdb_id=task['otdb_id']) + otdb_status = otdbrpc.taskGetStatus(task['otdb_id']) + + logger.info('waiting for otdb/radb task status to be in [%s].... otdb:%s radb:%s', + ', '.join(expected_statuses), otdb_status, task['status']) - if org_task['status'] == updatedTask['status'] and details['object_status'] == updatedTask['status']: + if (task['status'] in expected_statuses and + otdb_status in expected_statuses): break if datetime.utcnow() - start_wait > timedelta(seconds=10): @@ -459,12 +529,16 @@ def putTask(task_id): if 'does not exist' in e.message: # task does not exist (anymore) in otdb #so remove it from radb as well (with cascading deletes on specification) - logger.warn('task with otdb_id %s does not exist anymore in OTDB. removing task radb_id %s from radb', org_task['otdb_id'], org_task['id']) - radb().deleteSpecification(org_task['specification_id']) + logger.warn('task with otdb_id %s does not exist anymore in OTDB. removing task radb_id %s from radb', task['otdb_id'], task['id']) + radb().deleteSpecification(task['specification_id']) if 'data_pinned' in updatedTask: - if not curpc.setTaskDataPinned(org_task['otdb_id'], updatedTask['data_pinned']): - abort(500, 'Could not (un)pin task') + task = radb().getTask(task_id) + + if not task: + abort(404, "unknown task %s" % str(updatedTask)) + + curpc.setTaskDataPinned(task['otdb_id'], updatedTask['data_pinned']) return "", 204 except Exception as e: @@ -494,7 +568,8 @@ def cleanupTaskData(task_id): delete_im=delete_params.get('delete_im', True), delete_img=delete_params.get('delete_img', True), delete_pulp=delete_params.get('delete_pulp', True), - delete_scratch=delete_params.get('delete_scratch', True)) + delete_scratch=delete_params.get('delete_scratch', True), + force=delete_params.get('force_delete', False)) logger.info(result) return jsonify(result) except Exception as e: @@ -590,7 +665,7 @@ def getParsetByOTDBId(otdb_id): @app.route('/rest/tasks/<int:task_id>/resourceclaims') @gzipped def taskResourceClaims(task_id): - return jsonify({'taskResourceClaims': radb().getResourceClaims(task_id=task_id, include_properties=True)}) + return jsonify({'taskResourceClaims': radb().getResourceClaims(task_ids=[task_id], include_properties=True)}) @app.route('/rest/tasktypes') @gzipped @@ -634,6 +709,7 @@ def getProjects(): projects.append({'name':'<unknown>', 'mom_id':-99, 'description': 'Container project for tasks for which we could not find a MoM project'}) projects.append({'name':'OTDB Only', 'mom_id':-98, 'description': 'Container project for tasks which exists only in OTDB'}) + projects.append({'name':'Reservations', 'mom_id':-97, 'description': 'Container project for reservation tasks'}) return jsonify({'momprojects': projects}) @app.route('/rest/projects/<int:project_mom2id>') @@ -668,10 +744,7 @@ def getProjectTasksFromUntil(project_mom2id, fromTimestamp=None, untilTimestamp= task_mom2ids = momqueryrpc.getProjectTaskIds(project_mom2id)['task_mom2ids'] tasks = radb().getTasks(mom_ids=task_mom2ids, lower_bound=fromTimestamp, upper_bound=untilTimestamp) - - updateTaskMomDetails(tasks, momqueryrpc) - updateTaskStorageDetails(tasks, sqrpc, curpc) - + updateTaskDetails(tasks) return jsonify({'tasks': tasks}) except Exception as e: logger.error(e) @@ -777,8 +850,7 @@ def getTasksHtml(): if not tasks: abort(404) - updateTaskMomDetails(tasks, momqueryrpc) - updateTaskStorageDetails(tasks, sqrpc, curpc) + updateTaskDetails(tasks) html = '<!DOCTYPE html><html><head><title>Tasks</title><style>table, th, td {border: 1px solid black; border-collapse: collapse; padding: 4px;}</style></head><body><table style="width:100%">\n' @@ -809,8 +881,7 @@ def getTaskHtml(task_id): abort(404, 'No such task %s' % task_id) task['name'] = 'Task %d' % task['id'] - updateTaskMomDetails(task, momqueryrpc) - updateTaskStorageDetails(task, sqrpc, curpc) + updateTaskDetails([task]) html = '<!DOCTYPE html><html><head><title>Tasks</title><style>table, th, td {border: 1px solid black; border-collapse: collapse; padding: 4px;}</style></head><body><table style="">\n' diff --git a/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py b/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py index 1b5d5449ba5fff8eddf2a89c3db3d98a3d521b35..0143214ecc67d02dd4ead72fb250a0355f88a785 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py +++ b/SAS/ResourceAssignment/ResourceAssignmentService/rpc.py @@ -86,67 +86,82 @@ class RARPC(RPCWrapper): return resource_claim - def insertResourceClaim(self, resource_id, task_id, starttime, endtime, status, session_id, claim_size, username, + def insertResourceClaim(self, resource_id, task_id, starttime, endtime, status, claim_size, username, user_id, used_rcus=None, properties=None): return self.rpc('InsertResourceClaim', resource_id=resource_id, task_id=task_id, starttime=starttime, endtime=endtime, status=status, - session_id=session_id, claim_size=claim_size, username=username, used_rcus=used_rcus, user_id=user_id, properties=properties) - def insertResourceClaims(self, task_id, claims, session_id, username, user_id): + def insertResourceClaims(self, task_id, claims, username, user_id): return self.rpc('InsertResourceClaims', task_id=task_id, claims=claims, - session_id=session_id, username=username, user_id=user_id) def deleteResourceClaim(self, id): return self.rpc('DeleteResourceClaim', id=id) - def updateResourceClaim(self, id, resource_id=None, task_id=None, starttime=None, endtime=None, status=None, session_id=None, claim_size=None, username=None, used_rcus=None, user_id=None): + def updateResourceClaim(self, id, resource_id=None, task_id=None, starttime=None, endtime=None, status=None, claim_size=None, username=None, used_rcus=None, user_id=None): return self.rpc('UpdateResourceClaim', id=id, resource_id=resource_id, task_id=task_id, starttime=starttime, endtime=endtime, status=status, - session_id=session_id, claim_size=claim_size, username=username, used_rcus=used_rcus, user_id=user_id) - def updateTaskAndResourceClaims(self, task_id, starttime=None, endtime=None, task_status=None, claim_status=None, session_id=None, username=None, used_rcus=None, user_id=None): + def updateResourceClaims(self, where_resource_claim_ids=None, where_task_ids=None, where_resource_types=None, + resource_id=None, task_id=None, starttime=None, endtime=None, + status=None, claim_size=None, username=None, used_rcus=None, user_id=None, + commit=True): + return self.rpc('UpdateResourceClaims', where_resource_claim_ids=where_resource_claim_ids, + where_task_ids=where_task_ids, + where_resource_types=where_resource_types, + resource_id=resource_id, + task_id=task_id, + starttime=starttime, + endtime=endtime, + status=status, + claim_size=claim_size, + username=username, + used_rcus=used_rcus, + user_id=user_id) + + def updateTaskAndResourceClaims(self, task_id, starttime=None, endtime=None, task_status=None, claim_status=None, username=None, used_rcus=None, user_id=None, where_resource_types=None): return self.rpc('UpdateTaskAndResourceClaims', task_id=task_id, - starttime=starttime, - endtime=endtime, - task_status=task_status, - claim_status=claim_status, - session_id=session_id, - username=username, - used_rcus=used_rcus, - user_id=user_id) - - def getResourceUsages(self, claim_ids=None, lower_bound=None, upper_bound=None, resource_ids=None, task_ids=None, status=None, resource_type=None): + starttime=starttime, + endtime=endtime, + task_status=task_status, + claim_status=claim_status, + username=username, + used_rcus=used_rcus, + user_id=user_id, + where_resource_types=where_resource_types) + + + def getResourceUsages(self, lower_bound=None, upper_bound=None, resource_ids=None, status=None): all_usages = self.rpc('GetResourceUsages', lower_bound=lower_bound, upper_bound=upper_bound, resource_ids=resource_ids, - task_ids=task_ids, - status=status, - resource_type=resource_type) + status=status) - for resource_usages in all_usages: - for status, usages in resource_usages['usages'].items(): + all_usages = convertStringDigitKeysToInt(all_usages) + + for resource_id, resource_usages_per_status in all_usages.items(): + for status, usages in resource_usages_per_status.items(): for usage in usages: - usage['timestamp'] = usage['timestamp'].datetime() + usage['as_of_timestamp'] = usage['as_of_timestamp'].datetime() return all_usages @@ -304,20 +319,22 @@ class RARPC(RPCWrapper): def get_conflicting_overlapping_claims(self, claim_id): '''returns a list of claimed claims which overlap with given claim(s) and which prevent the given claim(s) to be claimed (cause it to be in conflict)''' - return self.rpc('get_conflicting_overlapping_claims', + return self.rpc('get_overlapping_claims', claim_id=claim_id) - def get_conflicting_overlapping_tasks(self, claim_ids): + def get_conflicting_overlapping_tasks(self, claim_id): '''returns a list of tasks which overlap with given claim(s) and which prevent the given claim(s) to be claimed (cause it to be in conflict)''' - return self.rpc('get_conflicting_overlapping_tasks', - claim_ids=claim_ids) + return self.rpc('get_overlapping_tasks', + claim_id=claim_id) def get_max_resource_usage_between(self, resource_id, lower_bound, upper_bound, claim_status='claimed'): - return self.rpc('get_max_resource_usage_between', - resource_id=resource_id, - lower_bound=lower_bound, - upper_bound=upper_bound, - claim_status=claim_status) + result = self.rpc('get_max_resource_usage_between', + resource_id=resource_id, + lower_bound=lower_bound, + upper_bound=upper_bound, + claim_status=claim_status) + result['as_of_timestamp'] = result['as_of_timestamp'].datetime() + return result def get_resource_claimable_capacity(self, resource_id, lower_bound, upper_bound): '''get the claimable capacity for the given resource within the timewindow given by lower_bound and upper_bound. diff --git a/SAS/ResourceAssignment/ResourceAssignmentService/service.py b/SAS/ResourceAssignment/ResourceAssignmentService/service.py index f2cfd92c144584fc93e4db5bfa5cbf2088795ab4..dd7e6a47a86fde3fe29a5a8b041eafd91aa7ec8e 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentService/service.py +++ b/SAS/ResourceAssignment/ResourceAssignmentService/service.py @@ -52,6 +52,7 @@ class RADBHandler(MessageHandlerInterface): 'InsertResourceClaim': self._insertResourceClaim, 'DeleteResourceClaim': self._deleteResourceClaim, 'UpdateResourceClaim': self._updateResourceClaim, + 'UpdateResourceClaims': self._updateResourceClaims, 'UpdateTaskAndResourceClaims': self._updateTaskAndResourceClaims, 'GetResourceUsages': self._getResourceUsages, 'GetResourceGroupTypes': self._getResourceGroupTypes, @@ -82,8 +83,8 @@ class RADBHandler(MessageHandlerInterface): 'UpdateSpecification': self._updateSpecification, 'GetUnits': self._getUnits, 'GetResourceAllocationConfig': self._getResourceAllocationConfig, - 'get_conflicting_overlapping_claims': self._get_conflicting_overlapping_claims, - 'get_conflicting_overlapping_tasks': self._get_conflicting_overlapping_tasks, + 'get_overlapping_claims': self._get_overlapping_claims, + 'get_overlapping_tasks': self._get_overlapping_tasks, 'get_max_resource_usage_between': self._get_max_resource_usage_between, 'get_resource_claimable_capacity': self._get_resource_claimable_capacity } @@ -147,7 +148,6 @@ class RADBHandler(MessageHandlerInterface): ids = self.radb.insertResourceClaims(kwargs['task_id'], claims, - kwargs['session_id'], kwargs['username'], kwargs['user_id']) return {'ids':ids} @@ -159,7 +159,6 @@ class RADBHandler(MessageHandlerInterface): kwargs['starttime'].datetime(), kwargs['endtime'].datetime(), kwargs.get('status_id', kwargs.get('status')), - kwargs['session_id'], kwargs['claim_size'], kwargs['username'], kwargs['user_id'], @@ -182,12 +181,33 @@ class RADBHandler(MessageHandlerInterface): starttime=kwargs.get('starttime').datetime() if kwargs.get('starttime') else None, endtime=kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, status=kwargs.get('status_id', kwargs.get('status')), - session_id=kwargs.get('session_id'), claim_size=kwargs.get('claim_size'), username=kwargs.get('username'), user_id=kwargs.get('user_id')) return {'id': id, 'updated': updated} + def _updateResourceClaims(self, **kwargs): + logger.info('UpdateResourceClaims: %s' % dict({k:v for k,v in kwargs.items() if v != None})) + task_id = kwargs['task_id'] + + updated = self.radb.updateResourceClaims(where_resource_claim_ids=kwargs.get('where_resource_claim_ids'), + where_task_ids=kwargs.get('where_task_ids'), + where_resource_types=kwargs.get('where_resource_types'), + resource_id=kwargs.get('resource_id'), + task_id=kwargs.get('task_id'), + starttime=kwargs.get('starttime').datetime() if kwargs.get('starttime') else None, + endtime=kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, + status=kwargs.get('status_id', kwargs.get('status')), + claim_size=kwargs.get('status'), + username=kwargs.get('username'), + user_id=kwargs.get('user_id'), + used_rcus=None) + + return {'where_resource_claim_ids': kwargs.get('where_resource_claim_ids'), + 'where_task_ids': kwargs.get('where_task_ids'), + 'where_resource_types': kwargs.get('where_resource_types'), + 'updated': updated} + def _updateTaskAndResourceClaims(self, **kwargs): logger.info('UpdateTaskAndResourceClaims: %s' % dict({k:v for k,v in kwargs.items() if v != None})) task_id = kwargs['task_id'] @@ -197,20 +217,21 @@ class RADBHandler(MessageHandlerInterface): endtime=kwargs.get('endtime').datetime() if kwargs.get('endtime') else None, task_status=kwargs.get('task_status_id', kwargs.get('task_status')), claim_status=kwargs.get('claim_status_id', kwargs.get('claim_status')), - session_id=kwargs.get('session_id'), username=kwargs.get('username'), - user_id=kwargs.get('user_id')) - return {'task_id': task_id, 'updated': updated} + user_id=kwargs.get('user_id'), + where_resource_types=kwargs.get('where_resource_types'), + commit=kwargs.get('commit', True)) + + return {'task_id': task_id, + 'where_resource_types': kwargs.get('where_resource_types'), + 'updated': updated} def _getResourceUsages(self, **kwargs): - usages = self.radb.getResourceUsages(claim_ids=kwargs.get('claim_ids'), - lower_bound=kwargs.get('lower_bound').datetime() if kwargs.get('lower_bound') else None, + usages = self.radb.getResourceUsages(lower_bound=kwargs.get('lower_bound').datetime() if kwargs.get('lower_bound') else None, upper_bound=kwargs.get('upper_bound').datetime() if kwargs.get('upper_bound') else None, resource_ids=kwargs.get('resource_ids'), - task_ids=kwargs.get('task_ids'), - status=kwargs.get('status'), - resource_type=kwargs.get('resource_type')) - return usages + claim_statuses=kwargs.get('status')) + return convertIntKeysToString(usages) def _getResourceGroupTypes(self): return self.radb.getResourceGroupTypes() @@ -235,10 +256,11 @@ class RADBHandler(MessageHandlerInterface): include_availability=kwargs.get('include_availability', False)) def _updateResourceAvailability(self, **kwargs): - return self.radb.updateResourceAvailability(resource_id=kwargs['resource_id'], - active=kwargs.get('active'), - available_capacity=kwargs.get('available_capacity'), - total_capacity=kwargs.get('total_capacity')) + updated = self.radb.updateResourceAvailability(resource_id=kwargs['resource_id'], + active=kwargs.get('active'), + available_capacity=kwargs.get('available_capacity'), + total_capacity=kwargs.get('total_capacity')) + return {'resource_id': kwargs['resource_id'], 'updated': updated } def _getTasksTimeWindow(self, **kwargs): logger.info('GetTasksTimeWindow: %s' % dict({k:v for k,v in kwargs.items() if v != None})) @@ -362,18 +384,24 @@ class RADBHandler(MessageHandlerInterface): def _getResourceAllocationConfig(self, **kwargs): return self.radb.getResourceAllocationConfig(sql_like_name_pattern=kwargs.get('sql_like_name_pattern')) - def _get_conflicting_overlapping_claims(self, **kwargs): - return self.radb.get_conflicting_overlapping_claims(**kwargs) + def _get_overlapping_claims(self, **kwargs): + return self.radb.get_overlapping_claims(claim_id=kwargs.get('claim_id')) - def _get_conflicting_overlapping_tasks(self, **kwargs): - return self.radb.get_conflicting_overlapping_tasks(**kwargs) + def _get_overlapping_tasks(self, **kwargs): + return self.radb.get_overlapping_tasks(claim_id=kwargs.get('claim_id')) def _get_max_resource_usage_between(self, **kwargs): - return self.radb.get_max_resource_usage_between(**kwargs) + logger.info('get_max_resource_usage_between: %s' % kwargs) + return self.radb.get_max_resource_usage_between(resource_id=kwargs.get('resource_id'), + lower_bound=kwargs['lower_bound'].datetime() if kwargs.get('lower_bound') else None, + upper_bound=kwargs['upper_bound'].datetime() if kwargs.get('upper_bound') else None, + claim_status=kwargs.get('claim_status', 'claimed')) def _get_resource_claimable_capacity(self, **kwargs): - return self.radb.get_resource_claimable_capacity(**kwargs) - + logger.info('get_resource_claimable_capacity: %s' % kwargs) + return self.radb.get_resource_claimable_capacity(resource_id=kwargs.get('resource_id'), + lower_bound=kwargs['lower_bound'].datetime() if kwargs.get('lower_bound') else None, + upper_bound=kwargs['upper_bound'].datetime() if kwargs.get('upper_bound') else None) def createService(busname=DEFAULT_BUSNAME, servicename=DEFAULT_SERVICENAME, broker=None, dbcreds=None, log_queries=False, verbose=False):