class TestSet(BASE): __tablename__ = 'test_sets' id = sa.Column(sa.String(128), primary_key=True) description = sa.Column(sa.String(256)) test_path = sa.Column(sa.String(256)) driver = sa.Column(sa.String(128)) additional_arguments = sa.Column(fields.ListField()) cleanup_path = sa.Column(sa.String(128)) meta = sa.Column(fields.JsonField()) deployment_tags = sa.Column(ARRAY(sa.String(64))) tests = relationship('Test', backref='test_set', order_by='Test.name', cascade='delete') @property def frontend(self): return {'id': self.id, 'name': self.description} @classmethod def get_test_set(cls, session, test_set): return session.query(cls)\ .filter_by(id=test_set)\ .first()
class TestSet(BASE): __tablename__ = 'test_sets' id = sa.Column(sa.String(128), primary_key=True) description = sa.Column(sa.String(256)) test_path = sa.Column(sa.String(256)) driver = sa.Column(sa.String(128)) additional_arguments = sa.Column(fields.ListField()) cleanup_path = sa.Column(sa.String(128)) meta = sa.Column(fields.JsonField()) deployment_tags = sa.Column(ARRAY(sa.String(64))) test_runs_ordering_priority = sa.Column(sa.Integer) # list of test sets that cannot be executed simultaneously # with current test set exclusive_testsets = sa.Column(ARRAY(sa.String(128))) available_since_release = sa.Column(sa.String(64), default="") tests = relationship('Test', backref='test_set', order_by='Test.name', cascade='delete') @property def frontend(self): return {'id': self.id, 'name': self.description} @classmethod def get_test_set(cls, session, test_set): return session.query(cls)\ .filter_by(id=test_set)\ .first()
def upgrade(): ### commands auto generated by Alembic - please adjust! ### op.add_column( 'test_runs', sa.Column('test_set_id', sa.String(length=128), nullable=True)) op.add_column('test_runs', sa.Column('meta', fields.JsonField(), nullable=True)) op.add_column('test_runs', sa.Column('cluster_id', sa.Integer(), nullable=False)) op.drop_column('test_runs', u'type') op.drop_column('test_runs', u'stats') op.drop_column('test_runs', u'external_id') op.drop_column('test_runs', u'data') op.alter_column('test_runs', 'status', existing_type=sa.VARCHAR(length=128), nullable=False) op.add_column( 'test_sets', sa.Column('cleanup_path', sa.String(length=128), nullable=True)) op.add_column('test_sets', sa.Column('meta', fields.JsonField(), nullable=True)) op.add_column('test_sets', sa.Column('driver', sa.String(length=128), nullable=True)) op.add_column( 'test_sets', sa.Column('additional_arguments', fields.ListField(), nullable=True)) op.add_column('test_sets', sa.Column('test_path', sa.String(length=256), nullable=True)) op.drop_column('test_sets', u'data') op.add_column('tests', sa.Column('description', sa.Text(), nullable=True)) op.add_column('tests', sa.Column('traceback', sa.Text(), nullable=True)) op.add_column('tests', sa.Column('step', sa.Integer(), nullable=True)) op.add_column('tests', sa.Column('meta', fields.JsonField(), nullable=True)) op.add_column('tests', sa.Column('duration', sa.String(length=512), nullable=True)) op.add_column('tests', sa.Column('message', sa.Text(), nullable=True)) op.add_column('tests', sa.Column('time_taken', sa.Float(), nullable=True)) op.drop_column('tests', u'taken') op.drop_column('tests', u'data')
class TestRun(BASE): __tablename__ = 'test_runs' STATES = ('running', 'finished') id = sa.Column(sa.Integer(), primary_key=True) cluster_id = sa.Column(sa.Integer(), nullable=False) status = sa.Column(sa.Enum(*STATES, name='test_run_states'), nullable=False) meta = sa.Column(fields.JsonField()) started_at = sa.Column(sa.DateTime, default=datetime.datetime.utcnow) ended_at = sa.Column(sa.DateTime) pid = sa.Column(sa.Integer) test_set_id = sa.Column(sa.String(128)) cluster_id = sa.Column(sa.Integer) __table_args__ = (sa.ForeignKeyConstraint(['test_set_id', 'cluster_id'], [ 'cluster_testing_pattern.test_set_id', 'cluster_testing_pattern.cluster_id' ], ondelete='CASCADE'), {}) cluster_testing_pattern = relationship('ClusterTestingPattern') test_set = association_proxy('cluster_testing_pattern', 'test_set') tests = relationship('Test', backref='test_run', order_by='Test.name', cascade='delete') def update(self, status): self.status = status if status == 'finished': self.ended_at = datetime.datetime.utcnow() @property def enabled_tests(self): return [test.name for test in self.tests if test.status != 'disabled'] def is_finished(self): return self.status == 'finished' @property def frontend(self): test_run_data = { 'id': self.id, 'testset': self.test_set_id, 'meta': self.meta, 'cluster_id': self.cluster_id, 'status': self.status, 'started_at': self.started_at, 'ended_at': self.ended_at, 'tests': [] } if self.tests: test_run_data['tests'] = [test.frontend for test in self.tests] return test_run_data @classmethod def add_test_run(cls, session, test_set, cluster_id, status='running', tests=None): """Creates new test_run object with given data and makes copy of tests that will be bound with this test_run. Copying is performed by copy_test method of Test class. """ predefined_tests = tests or [] tests_names = session.query(ClusterTestingPattern.tests)\ .filter_by(test_set_id=test_set, cluster_id=cluster_id)\ .scalar() tests = session.query(Test)\ .filter(Test.name.in_(tests_names))\ .filter_by(test_set_id=test_set)\ .filter_by(test_run_id=None) test_run = cls(test_set_id=test_set, cluster_id=cluster_id, status=status) session.add(test_run) for test in tests: new_test = test.copy_test(test_run, predefined_tests) session.add(new_test) test_run.tests.append(new_test) session.flush() return test_run @classmethod def get_last_test_run(cls, session, test_set, cluster_id): test_run = session.query(cls). \ filter_by(cluster_id=cluster_id, test_set_id=test_set). \ order_by(desc(cls.id)).first() return test_run @classmethod def get_test_results(cls): session = engine.get_session() test_runs = session.query(cls). \ options(joinedload('tests')). \ order_by(desc(cls.id)) session.commit() session.close() return test_runs @classmethod def get_test_run(cls, session, test_run_id, joined=False): if not joined: test_run = session.query(cls). \ filter_by(id=test_run_id).first() else: test_run = session.query(cls). \ options(joinedload('tests')). \ filter_by(id=test_run_id).first() return test_run @classmethod def update_test_run(cls, session, test_run_id, updated_data): if updated_data.get('status') in ['finished']: updated_data['ended_at'] = datetime.datetime.utcnow() session.query(cls). \ filter(cls.id == test_run_id). \ update(updated_data, synchronize_session='fetch') @classmethod def is_last_running(cls, session, test_set, cluster_id): """Checks whether there one can perform creation of new test_run by testing of existing of test_run object with given data or test_run with 'finished' status. """ test_run = cls.get_last_test_run(session, test_set, cluster_id) return not bool(test_run) or test_run.is_finished() @classmethod def start(cls, session, test_set, metadata, tests, dbpath, token=None): plugin = nose_plugin.get_plugin(test_set.driver) if cls.is_last_running(session, test_set.id, metadata['cluster_id']): test_run = cls.add_test_run(session, test_set.id, metadata['cluster_id'], tests=tests) plugin.run(test_run, test_set, dbpath, metadata.get('ostf_os_access_creds'), token=token) return test_run.frontend return {} def restart(self, session, dbpath, ostf_os_access_creds, tests=None, token=None): """Restart test run with if tests given they will be enabled """ if TestRun.is_last_running(session, self.test_set_id, self.cluster_id): plugin = nose_plugin.get_plugin(self.test_set.driver) self.update('running') if tests: Test.update_test_run_tests(session, self.id, tests) plugin.run(self, self.test_set, dbpath, ostf_os_access_creds, tests, token=token) return self.frontend return {} def stop(self, session): """Stop test run if running """ plugin = nose_plugin.get_plugin(self.test_set.driver) killed = plugin.kill(self) if killed: Test.update_running_tests(session, self.id, status='stopped') return self.frontend
class Test(BASE): __tablename__ = 'tests' STATES = ('wait_running', 'running', 'failure', 'success', 'error', 'stopped', 'disabled', 'skipped') id = sa.Column(sa.Integer(), primary_key=True) name = sa.Column(sa.String(512)) title = sa.Column(sa.String(512)) description = sa.Column(sa.Text()) duration = sa.Column(sa.String(512)) message = sa.Column(sa.Text()) traceback = sa.Column(sa.Text()) status = sa.Column(sa.Enum(*STATES, name='test_states')) step = sa.Column(sa.Integer()) time_taken = sa.Column(sa.Float()) meta = sa.Column(fields.JsonField()) deployment_tags = sa.Column(ARRAY(sa.String(64))) available_since_release = sa.Column(sa.String(64), default="") test_run_id = sa.Column(sa.Integer(), sa.ForeignKey('test_runs.id', ondelete='CASCADE')) test_set_id = sa.Column(sa.String(length=128), sa.ForeignKey('test_sets.id', ondelete='CASCADE')) @property def frontend(self): return { 'id': self.name, 'testset': self.test_set_id, 'name': self.title, 'description': self.description, 'duration': self.duration, 'message': self.message, 'step': self.step, 'status': self.status, 'taken': self.time_taken } @classmethod def add_result(cls, session, test_run_id, test_name, data): session.query(cls).\ filter(cls.name == test_name, cls.test_run_id == test_run_id).\ update(data, synchronize_session='fetch') @classmethod def update_running_tests(cls, session, test_run_id, status='stopped'): session.query(cls). \ filter(cls.test_run_id == test_run_id, cls.status.in_(('running', 'wait_running'))). \ update({'status': status}, synchronize_session='fetch') @classmethod def update_test_run_tests(cls, session, test_run_id, tests_names, status='wait_running'): session.query(cls). \ filter(cls.name.in_(tests_names), cls.test_run_id == test_run_id). \ update({'status': status, 'time_taken': None}, synchronize_session='fetch') def copy_test(self, test_run, predefined_tests): """Performs copying of tests for newly created test_run. """ new_test = self.__class__() mapper = object_mapper(self) primary_keys = set([col.key for col in mapper.primary_key]) for column in mapper.iterate_properties: if column.key not in primary_keys: setattr(new_test, column.key, getattr(self, column.key)) new_test.test_run_id = test_run.id if predefined_tests and new_test.name not in predefined_tests: new_test.status = 'disabled' else: new_test.status = 'wait_running' return new_test
def upgrade(): op.create_table( 'cluster_state', sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('deployment_tags', postgresql.ARRAY(sa.String(length=64)), nullable=True), sa.PrimaryKeyConstraint('id') ) op.create_table( 'test_sets', sa.Column('id', sa.String(length=128), nullable=False), sa.Column('description', sa.String(length=256), nullable=True), sa.Column('test_path', sa.String(length=256), nullable=True), sa.Column('driver', sa.String(length=128), nullable=True), sa.Column('additional_arguments', fields.ListField(), nullable=True), sa.Column('cleanup_path', sa.String(length=128), nullable=True), sa.Column('meta', fields.JsonField(), nullable=True), sa.Column('deployment_tags', postgresql.ARRAY(sa.String(length=64)), nullable=True), sa.Column('test_runs_ordering_priority', sa.Integer(), nullable=True), sa.PrimaryKeyConstraint('id') ) op.create_table( 'cluster_testing_pattern', sa.Column('cluster_id', sa.Integer(), nullable=False), sa.Column('test_set_id', sa.String(length=128), nullable=False), sa.Column('tests', postgresql.ARRAY(sa.String(length=512)), nullable=True), sa.ForeignKeyConstraint(['cluster_id'], ['cluster_state.id'], ), sa.ForeignKeyConstraint(['test_set_id'], ['test_sets.id'], ), sa.PrimaryKeyConstraint('cluster_id', 'test_set_id') ) op.create_table( 'test_runs', sa.Column('id', sa.Integer(), nullable=False), sa.Column('status', sa.Enum('running', 'finished', name='test_run_states'), nullable=False), sa.Column('meta', fields.JsonField(), nullable=True), sa.Column('started_at', sa.DateTime(), nullable=True), sa.Column('ended_at', sa.DateTime(), nullable=True), sa.Column('test_set_id', sa.String(length=128), nullable=True), sa.Column('cluster_id', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['test_set_id', 'cluster_id'], ['cluster_testing_pattern.test_set_id', 'cluster_testing_pattern.cluster_id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') ) op.create_table( 'tests', sa.Column('id', sa.Integer(), nullable=False), sa.Column('name', sa.String(length=512), nullable=True), sa.Column('title', sa.String(length=512), nullable=True), sa.Column('description', sa.Text(), nullable=True), sa.Column('duration', sa.String(length=512), nullable=True), sa.Column('message', sa.Text(), nullable=True), sa.Column('traceback', sa.Text(), nullable=True), sa.Column('status', sa.Enum('wait_running', 'running', 'failure', 'success', 'error', 'stopped', 'disabled', 'skipped', name='test_states'), nullable=True), sa.Column('step', sa.Integer(), nullable=True), sa.Column('time_taken', sa.Float(), nullable=True), sa.Column('meta', fields.JsonField(), nullable=True), sa.Column('deployment_tags', postgresql.ARRAY(sa.String(length=64)), nullable=True), sa.Column('test_run_id', sa.Integer(), nullable=True), sa.Column('test_set_id', sa.String(length=128), nullable=True), sa.ForeignKeyConstraint(['test_run_id'], ['test_runs.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['test_set_id'], ['test_sets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id') )
class TestRun(BASE): __tablename__ = 'test_runs' STATES = ('running', 'finished') id = sa.Column(sa.Integer(), primary_key=True) cluster_id = sa.Column(sa.Integer(), nullable=False) status = sa.Column(sa.Enum(*STATES, name='test_run_states'), nullable=False) meta = sa.Column(fields.JsonField()) started_at = sa.Column(sa.DateTime, default=datetime.utcnow) ended_at = sa.Column(sa.DateTime) test_set_id = sa.Column(sa.String(128), sa.ForeignKey('test_sets.id')) test_set = relationship('TestSet', backref='test_runs') tests = relationship('Test', backref='test_run', order_by='Test.name') def update(self, session, status): self.status = status if status == 'finished': self.ended_at = datetime.utcnow() session.add(self) @property def enabled_tests(self): return [test.name for test in self.tests if test.status != 'disabled'] def is_finished(self): return self.status == 'finished' @property def frontend(self): test_run_data = { 'id': self.id, 'testset': self.test_set_id, 'meta': self.meta, 'cluster_id': self.cluster_id, 'status': self.status, 'started_at': self.started_at, 'ended_at': self.ended_at, 'tests': [] } if self.tests: test_run_data['tests'] = [test.frontend for test in self.tests] return test_run_data @classmethod def add_test_run(cls, session, test_set, cluster_id, status='running', tests=None): predefined_tests = tests or [] tests = session.query(Test).filter_by(test_set_id=test_set, test_run_id=None) test_run = cls(test_set_id=test_set, cluster_id=cluster_id, status=status) session.add(test_run) for test in tests: session.add(test.copy_test(test_run, predefined_tests)) return test_run @classmethod def get_last_test_run(cls, session, test_set, cluster_id): test_run = session.query(cls). \ filter_by(cluster_id=cluster_id, test_set_id=test_set). \ order_by(desc(cls.id)).first() return test_run @classmethod def get_test_results(cls): session = engine.get_session() test_runs = session.query(cls). \ options(joinedload('tests')). \ order_by(desc(cls.id)) session.commit() session.close() return test_runs @classmethod def get_test_run(cls, session, test_run_id, joined=False): if not joined: test_run = session.query(cls). \ filter_by(id=test_run_id).first() else: test_run = session.query(cls). \ options(joinedload('tests')). \ filter_by(id=test_run_id).first() return test_run @classmethod def update_test_run(cls, session, test_run_id, status=None): updated_data = {} if status: updated_data['status'] = status if status in ['finished']: updated_data['ended_at'] = datetime.utcnow() session.query(cls). \ filter(cls.id == test_run_id). \ update(updated_data, synchronize_session=False) @classmethod def is_last_running(cls, session, test_set, cluster_id): test_run = cls.get_last_test_run(session, test_set, cluster_id) return not bool(test_run) or test_run.is_finished() @classmethod def start(cls, session, test_set, metadata, tests): plugin = nose_plugin.get_plugin(test_set.driver) if cls.is_last_running(session, test_set.id, metadata['cluster_id']): test_run = cls.add_test_run(session, test_set.id, metadata['cluster_id'], tests=tests) plugin.run(test_run, test_set) return test_run.frontend return {} def restart(self, session, tests=None): """Restart test run with if tests given they will be enabled """ if TestRun.is_last_running(session, self.test_set_id, self.cluster_id): plugin = nose_plugin.get_plugin(self.test_set.driver) self.update(session, 'running') if tests: Test.update_test_run_tests(session, self.id, tests) plugin.run(self, self.test_set, tests) return self.frontend return {} def stop(self, session): """Stop test run if running """ plugin = nose_plugin.get_plugin(self.test_set.driver) killed = plugin.kill(self.id, self.cluster_id, cleanup=self.test_set.cleanup_path) if killed: Test.update_running_tests(session, self.id, status='stopped') return self.frontend