@@ -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 },