@@ -320,7 +320,7 @@ class BoardImpl:
self.setupscript = setupscript
self.setupscript = setupscript
self.options = options
self.options = options
self.reserved = False
self.reserved = False
self.user = None
self._ user = None
self.attrmap = {}
self.attrmap = {}
self.lock = asyncio.Lock()
self.lock = asyncio.Lock()
for i in options:
for i in options:
@@ -364,7 +364,7 @@ class BoardImpl:
self.add_info(json.loads(stdout))
self.add_info(json.loads(stdout))
self.user = user
self._ user = user
self.reserved = True
self.reserved = True
await self.activate()
await self.activate()
@@ -400,6 +400,7 @@ class BoardImpl:
self.clean_info()
self.clean_info()
self._user = None
self.reserved = False
self.reserved = False
async def update_attrs(self, **attrs):
async def update_attrs(self, **attrs):
@@ -470,6 +471,10 @@ class BoardImpl:
for i in set(self.attrcache) - set(self.attrmap):
for i in set(self.attrcache) - set(self.attrmap):
del self.attrcache[i]
del self.attrcache[i]
@property
def user(self):
return self._user
@property
@property
def attrs(self):
def attrs(self):
return dict(self.attrcache)
return dict(self.attrcache)
@@ -597,11 +602,6 @@ def get_data(settings: config.Settings = Depends(get_settings)):
async def real_get_boardmanager(settings, data):
async def real_get_boardmanager(settings, data):
brdmgr = BoardManager.from_settings(settings)
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
return brdmgr
_global_lock = asyncio.Lock()
_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:
if user is None:
user = await lookup_user(token, data)
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(
raise BITEError(
status_code=HTTP_400_BAD_REQUEST,
status_code=HTTP_400_BAD_REQUEST,
errobj=Error(error='Board not reserved.',
errobj=Error(error='Board not reserved.',
board=Board.from_orm(brd)))
board=Board.from_orm(brd)))
if user != brduser .user:
if user != brd.user:
raise BITEError(
raise BITEError(
status_code=HTTP_403_FORBIDDEN,
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)))
board=Board.from_orm(brd)))
yield brd
yield brd
@@ -725,18 +723,7 @@ async def reserve_board(board_id_or_class,
brd = brdmgr.boards[board_id]
brd = brdmgr.boards[board_id]
async with brd.lock:
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(
raise BITEError(
status_code=HTTP_409_CONFLICT,
status_code=HTTP_409_CONFLICT,
errobj=Error(error='Board currently reserved.',
errobj=Error(error='Board currently reserved.',
@@ -747,7 +734,6 @@ async def reserve_board(board_id_or_class,
try:
try:
await brd.reserve(user, sshpubkey)
await brd.reserve(user, sshpubkey)
except Exception as e:
except Exception as e:
await brdreq.delete()
if isinstance(e, RuntimeError):
if isinstance(e, RuntimeError):
retcode, stderr = e.args
retcode, stderr = e.args
raise BITEError(
raise BITEError(
@@ -890,21 +876,19 @@ async def release_board(board_id, user: str = Depends(lookup_user),
# XXX - how to handle a release error?
# XXX - how to handle a release error?
await log_event('release', user=user, board=brd)
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(
raise BITEError(
status_code=HTTP_400_BAD_REQUEST,
status_code=HTTP_400_BAD_REQUEST,
errobj=Error(error='Board not reserved.',
errobj=Error(error='Board not reserved.',
board=Board.from_orm(brd)),
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:
try:
await brd.release()
await brd.release()
except RuntimeError as e:
except RuntimeError as e:
@@ -914,8 +898,6 @@ async def release_board(board_id, user: str = Depends(lookup_user),
board=Board.from_orm(brd)),
board=Board.from_orm(brd)),
)
)
await data.BoardStatus.delete(brduser)
await brd.update()
await brd.update()
return brd
return brd
@@ -1040,9 +1022,16 @@ class TestWebSocket(TestCommon):
shutil.rmtree(self.basetempdir)
shutil.rmtree(self.basetempdir)
self.basetempdir = None
self.basetempdir = None
@patch('bitelab.snmp.snmpset')
@patch('bitelab.snmp.snmpget')
@patch('asyncio.create_subprocess_exec')
@patch('asyncio.create_subprocess_exec')
@timeout(2)
@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):
def wrapper(corofun):
async def foo(*args, **kwargs):
async def foo(*args, **kwargs):
r = await corofun(*args, **kwargs)
r = await corofun(*args, **kwargs)
@@ -1077,8 +1066,6 @@ class TestWebSocket(TestCommon):
# that when the board is reserved by the wrong user
# that when the board is reserved by the wrong user
wrap_subprocess_exec(cse, stdout=b'{}', retcode=0)
wrap_subprocess_exec(cse, stdout=b'{}', retcode=0)
brd = self.brdmgr.boards['cora-1']
brd = self.brdmgr.boards['cora-1']
obrdreq = await self.data.BoardStatus.objects.create(
board='cora-1', user='bar')
async with brd.lock:
async with brd.lock:
await brd.reserve('bar')
await brd.reserve('bar')
@@ -1086,12 +1073,10 @@ class TestWebSocket(TestCommon):
with self.assertRaisesRegex(RuntimeError, 'Board reserved by \'bar\'.'):
with self.assertRaisesRegex(RuntimeError, 'Board reserved by \'bar\'.'):
await client.exec([ 'sshd', '-i' ], stdin=1, stdout=2)
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
# 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'
echodata = b'somedata'
wrap_subprocess_exec(cse, stdout=echodata, retcode=0)
wrap_subprocess_exec(cse, stdout=echodata, retcode=0)
@@ -1197,8 +1182,6 @@ class TestBiteLabAPI(TestCommon):
attrs = dict(iface='a', ip='b', devfsrule='c')
attrs = dict(iface='a', ip='b', devfsrule='c')
async with brd.lock:
async with brd.lock:
await brd.reserve('foo')
await brd.reserve('foo')
obrdreq = await data.BoardStatus.objects.create(
board='cora-1', user='foo')
brd.attrcache.update(attrs)
brd.attrcache.update(attrs)
# that when the script fails
# that when the script fails
@@ -1214,6 +1197,7 @@ class TestBiteLabAPI(TestCommon):
# and returns the correct data
# and returns the correct data
info = Error(error='Failed to release board, ret: 1, stderr: b\'error\'',
info = Error(error='Failed to release board, ret: 1, stderr: b\'error\'',
board=Board(name='cora-1',
board=Board(name='cora-1',
user='foo',
brdclass='cora-z7s',
brdclass='cora-z7s',
reserved=True,
reserved=True,
attrs=attrs,
attrs=attrs,
@@ -1291,6 +1275,7 @@ class TestBiteLabAPI(TestCommon):
# and returns the correct data
# and returns the correct data
brdinfo = Board(name='cora-1',
brdinfo = Board(name='cora-1',
user='foo',
brdclass='cora-z7s',
brdclass='cora-z7s',
reserved=True,
reserved=True,
attrs=dict(power=False,
attrs=dict(power=False,
@@ -1351,6 +1336,7 @@ class TestBiteLabAPI(TestCommon):
# and returns the correct data
# and returns the correct data
info = {
info = {
'name': 'cora-1',
'name': 'cora-1',
'user': None,
'brdclass': 'cora-z7s',
'brdclass': 'cora-z7s',
'reserved': False,
'reserved': False,
'attrs': { 'power': False },
'attrs': { 'power': False },
@@ -1402,6 +1388,7 @@ class TestBiteLabAPI(TestCommon):
info = {
info = {
'cora-1': {
'cora-1': {
'name': 'cora-1',
'name': 'cora-1',
'user': None,
'brdclass': 'cora-z7s',
'brdclass': 'cora-z7s',
'reserved': False,
'reserved': False,
'attrs': { 'power': False },
'attrs': { 'power': False },
@@ -1426,6 +1413,7 @@ class TestBiteLabAPI(TestCommon):
# and returns the correct data
# and returns the correct data
info = {
info = {
'name': 'cora-1',
'name': 'cora-1',
'user': None,
'brdclass': 'cora-z7s',
'brdclass': 'cora-z7s',
'reserved': False,
'reserved': False,
'attrs': { 'power': True },
'attrs': { 'power': True },
@@ -1465,8 +1453,6 @@ class TestBiteLabAPI(TestCommon):
brd = self.brdmgr.boards['cora-1']
brd = self.brdmgr.boards['cora-1']
async with brd.lock:
async with brd.lock:
await brd.reserve('foo')
await brd.reserve('foo')
obrdreq = await data.BoardStatus.objects.create(
board='cora-1', user='foo')
# that setting the board attributes
# that setting the board attributes
res = await self.client.post('/board/cora-1/attrs',
res = await self.client.post('/board/cora-1/attrs',
@@ -1483,6 +1469,7 @@ class TestBiteLabAPI(TestCommon):
# and returns the correct data
# and returns the correct data
info = {
info = {
'name': 'cora-1',
'name': 'cora-1',
'user': 'foo',
'brdclass': 'cora-z7s',
'brdclass': 'cora-z7s',
'reserved': True,
'reserved': True,
'attrs': { 'power': False },
'attrs': { 'power': False },
@@ -1509,6 +1496,7 @@ class TestBiteLabAPI(TestCommon):
# and returns the correct data
# and returns the correct data
info = {
info = {
'name': 'cora-1',
'name': 'cora-1',
'user': 'foo',
'brdclass': 'cora-z7s',
'brdclass': 'cora-z7s',
'reserved': True,
'reserved': True,
'attrs': { 'power': True },
'attrs': { 'power': True },