From d89c90ffda5ce3c4baeaa824eae2ccbde000b35c Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Tue, 22 Dec 2020 17:51:26 -0800 Subject: [PATCH] drop BoardStatus from the db.. all user is managed by BoardImpl now.. This may come back in the future, but it will be in BoardImpl, and will contain more state such that a resume will have all the attribute's current state in addition to checkout and the like. --- bitelab/__init__.py | 84 +++++++++++++++++++-------------------------- bitelab/data.py | 10 +----- 2 files changed, 37 insertions(+), 57 deletions(-) diff --git a/bitelab/__init__.py b/bitelab/__init__.py index f2ac26f..ca56147 100644 --- a/bitelab/__init__.py +++ b/bitelab/__init__.py @@ -320,7 +320,7 @@ class BoardImpl: self.setupscript = setupscript self.options = options self.reserved = False - self.user = None + self._user = None self.attrmap = {} self.lock = asyncio.Lock() for i in options: @@ -364,7 +364,7 @@ class BoardImpl: self.add_info(json.loads(stdout)) - self.user = user + self._user = user self.reserved = True await self.activate() @@ -400,6 +400,7 @@ class BoardImpl: self.clean_info() + self._user = None self.reserved = False async def update_attrs(self, **attrs): @@ -470,6 +471,10 @@ class BoardImpl: for i in set(self.attrcache) - set(self.attrmap): del self.attrcache[i] + @property + def user(self): + return self._user + @property def attrs(self): return dict(self.attrcache) @@ -597,11 +602,6 @@ def get_data(settings: config.Settings = Depends(get_settings)): async def real_get_boardmanager(settings, data): brdmgr = BoardManager.from_settings(settings) - # Clean up the database - # XXX - This isn't a complete fix, we need a better solution. - all = await data.BoardStatus.objects.all() - await asyncio.gather(*(x.delete() for x in all)) - return brdmgr _global_lock = asyncio.Lock() @@ -647,18 +647,16 @@ async def validate_board_params(board_id, data, brdmgr, user=None, token=None): if user is None: user = await lookup_user(token, data) - try: - brduser = await data.BoardStatus.objects.get(board=board_id) - except orm.exceptions.NoMatch: + if brd.user is None: raise BITEError( status_code=HTTP_400_BAD_REQUEST, errobj=Error(error='Board not reserved.', board=Board.from_orm(brd))) - if user != brduser.user: + if user != brd.user: raise BITEError( status_code=HTTP_403_FORBIDDEN, - errobj=Error(error='Board reserved by %s.' % repr(brduser.user), + errobj=Error(error='Board reserved by %s.' % repr(brd.user), board=Board.from_orm(brd))) yield brd @@ -725,18 +723,7 @@ async def reserve_board(board_id_or_class, brd = brdmgr.boards[board_id] async with brd.lock: - try: - obrdreq = await data.BoardStatus.objects.create(board=board_id, - user=user) - # XXX - There is a bug in orm where the returned - # object has an incorrect board value - # see: https://github.com/encode/orm/issues/47 - #assert obrdreq.board == board_id and \ - # obrdreq.user == user - brdreq = await data.BoardStatus.objects.get(board=board_id, - user=user) - # XXX - orm isn't doing it's job here - except sqlite3.IntegrityError: + if brd.user is not None: raise BITEError( status_code=HTTP_409_CONFLICT, errobj=Error(error='Board currently reserved.', @@ -747,7 +734,6 @@ async def reserve_board(board_id_or_class, try: await brd.reserve(user, sshpubkey) except Exception as e: - await brdreq.delete() if isinstance(e, RuntimeError): retcode, stderr = e.args raise BITEError( @@ -890,21 +876,19 @@ async def release_board(board_id, user: str = Depends(lookup_user), # XXX - how to handle a release error? await log_event('release', user=user, board=brd) - try: - brduser = await data.BoardStatus.objects.get(board=board_id) - if user != brduser.user: - raise BITEError( - status_code=HTTP_403_FORBIDDEN, - errobj=Error(error='Board reserved by %s.' % repr(brduser.user), - board=Board.from_orm(brd))) - - except orm.exceptions.NoMatch: + if brd.user is None: raise BITEError( status_code=HTTP_400_BAD_REQUEST, errobj=Error(error='Board not reserved.', board=Board.from_orm(brd)), ) + if user != brd.user: + raise BITEError( + status_code=HTTP_403_FORBIDDEN, + errobj=Error(error='Board reserved by %s.' % repr(brd.user), + board=Board.from_orm(brd))) + try: await brd.release() except RuntimeError as e: @@ -914,8 +898,6 @@ async def release_board(board_id, user: str = Depends(lookup_user), board=Board.from_orm(brd)), ) - await data.BoardStatus.delete(brduser) - await brd.update() return brd @@ -1040,9 +1022,16 @@ class TestWebSocket(TestCommon): shutil.rmtree(self.basetempdir) self.basetempdir = None + @patch('bitelab.snmp.snmpset') + @patch('bitelab.snmp.snmpget') @patch('asyncio.create_subprocess_exec') @timeout(2) - async def test_exec_sshd(self, cse): + async def test_exec_sshd(self, cse, sg, ss): + + # that snmpget and snmpset returns False + sg.return_value = False + ss.return_value = False + def wrapper(corofun): async def foo(*args, **kwargs): r = await corofun(*args, **kwargs) @@ -1077,8 +1066,6 @@ class TestWebSocket(TestCommon): # that when the board is reserved by the wrong user wrap_subprocess_exec(cse, stdout=b'{}', retcode=0) brd = self.brdmgr.boards['cora-1'] - obrdreq = await self.data.BoardStatus.objects.create( - board='cora-1', user='bar') async with brd.lock: await brd.reserve('bar') @@ -1086,12 +1073,10 @@ class TestWebSocket(TestCommon): with self.assertRaisesRegex(RuntimeError, 'Board reserved by \'bar\'.'): await client.exec([ 'sshd', '-i' ], stdin=1, stdout=2) - brduser = await self.data.BoardStatus.objects.get(board='cora-1') - obrdreq = await self.data.BoardStatus.delete(brduser) - # that when the board is reserved by the correct user - obrdreq = await self.data.BoardStatus.objects.create( - board='cora-1', user='foo') + async with brd.lock: + await brd.release() + await brd.reserve('foo') echodata = b'somedata' wrap_subprocess_exec(cse, stdout=echodata, retcode=0) @@ -1197,8 +1182,6 @@ class TestBiteLabAPI(TestCommon): attrs = dict(iface='a', ip='b', devfsrule='c') async with brd.lock: await brd.reserve('foo') - obrdreq = await data.BoardStatus.objects.create( - board='cora-1', user='foo') brd.attrcache.update(attrs) # that when the script fails @@ -1214,6 +1197,7 @@ class TestBiteLabAPI(TestCommon): # and returns the correct data info = Error(error='Failed to release board, ret: 1, stderr: b\'error\'', board=Board(name='cora-1', + user='foo', brdclass='cora-z7s', reserved=True, attrs=attrs, @@ -1291,6 +1275,7 @@ class TestBiteLabAPI(TestCommon): # and returns the correct data brdinfo = Board(name='cora-1', + user='foo', brdclass='cora-z7s', reserved=True, attrs=dict(power=False, @@ -1351,6 +1336,7 @@ class TestBiteLabAPI(TestCommon): # and returns the correct data info = { 'name': 'cora-1', + 'user': None, 'brdclass': 'cora-z7s', 'reserved': False, 'attrs': { 'power': False }, @@ -1402,6 +1388,7 @@ class TestBiteLabAPI(TestCommon): info = { 'cora-1': { 'name': 'cora-1', + 'user': None, 'brdclass': 'cora-z7s', 'reserved': False, 'attrs': { 'power': False }, @@ -1426,6 +1413,7 @@ class TestBiteLabAPI(TestCommon): # and returns the correct data info = { 'name': 'cora-1', + 'user': None, 'brdclass': 'cora-z7s', 'reserved': False, 'attrs': { 'power': True }, @@ -1465,8 +1453,6 @@ class TestBiteLabAPI(TestCommon): brd = self.brdmgr.boards['cora-1'] async with brd.lock: await brd.reserve('foo') - obrdreq = await data.BoardStatus.objects.create( - board='cora-1', user='foo') # that setting the board attributes res = await self.client.post('/board/cora-1/attrs', @@ -1483,6 +1469,7 @@ class TestBiteLabAPI(TestCommon): # and returns the correct data info = { 'name': 'cora-1', + 'user': 'foo', 'brdclass': 'cora-z7s', 'reserved': True, 'attrs': { 'power': False }, @@ -1509,6 +1496,7 @@ class TestBiteLabAPI(TestCommon): # and returns the correct data info = { 'name': 'cora-1', + 'user': 'foo', 'brdclass': 'cora-z7s', 'reserved': True, 'attrs': { 'power': True }, diff --git a/bitelab/data.py b/bitelab/data.py index 6d7648c..201d16d 100644 --- a/bitelab/data.py +++ b/bitelab/data.py @@ -53,6 +53,7 @@ class BoardClassInfo(BaseModel): class Board(BaseModel): name: str + user: Optional[str] brdclass: str reserved: bool attrs: Dict[str, Any] = Field(default_factory=dict) @@ -76,15 +77,6 @@ class DataWrapper(object): def make_orm(database): metadata = sqlalchemy.MetaData() - class BoardStatus(orm.Model): - __tablename__ = 'boardstatus' - __database__ = database - __metadata__ = metadata - - board = orm.Text(primary_key=True) - user = orm.Text(index=True) - time_reserved = orm.DateTime(default=lambda: datetime.utcnow()) - class APIKey(orm.Model): __tablename__ = 'apikeys' __database__ = database