Browse Source

add support for activating resources, such as unhiding device nodes..

main
John-Mark Gurney 4 years ago
parent
commit
53f8c70e22
3 changed files with 89 additions and 6 deletions
  1. +70
    -5
      bitelab/__init__.py
  2. +18
    -0
      bitelab/abstract.py
  3. +1
    -1
      bitelab/testing.py

+ 70
- 5
bitelab/__init__.py View File

@@ -39,7 +39,7 @@ from starlette.status import HTTP_200_OK
from starlette.status import HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED, \ from starlette.status import HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED, \
HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_409_CONFLICT HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_409_CONFLICT
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR
from unittest.mock import patch, AsyncMock, Mock, PropertyMock
from unittest.mock import create_autospec, patch, AsyncMock, Mock, PropertyMock


from . import config from . import config
from .data import * from .data import *
@@ -76,6 +76,18 @@ tcp_server.parse_socket_addr = new_parse_socket_addr
class SerialConsole(DefROAttribute): class SerialConsole(DefROAttribute):
defattrname = 'console' defattrname = 'console'


async def activate(self, brd):
cmd = ('devfs', '-m', brd.attrs['devfspath'], 'rule', 'apply',
'path', os.path.basename(self._value), 'unhide', )
sub = await asyncio.create_subprocess_exec(*cmd,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)

ret = await sub.wait()

if ret:
raise RuntimeError('activate failed: %d' % ret)

class BoardImpl: class BoardImpl:
def __init__(self, name, brdclass, options): def __init__(self, name, brdclass, options):
self.name = name self.name = name
@@ -110,6 +122,14 @@ class BoardImpl:
for i in self.attrmap: for i in self.attrmap:
self.attrcache[i] = await self.attrmap[i].getvalue() self.attrcache[i] = await self.attrmap[i].getvalue()


async def activate(self):
for i in self.options:
await i.activate(self)

async def deactivate(self):
for i in self.options:
await i.deactivate(self)

def add_info(self, d): def add_info(self, d):
self.attrcache.update(d) self.attrcache.update(d)


@@ -353,8 +373,6 @@ async def reserve_board(board_id_or_class,
stdout, stderr = await sub.communicate() stdout, stderr = await sub.communicate()
if sub.returncode: if sub.returncode:
raise RuntimeError(sub.returncode, stderr) raise RuntimeError(sub.returncode, stderr)

brd.add_info(json.loads(stdout))
except Exception as e: except Exception as e:
await brdreq.delete() await brdreq.delete()
await brd.release() await brd.release()
@@ -369,6 +387,10 @@ async def reserve_board(board_id_or_class,
) )
raise raise


brd.add_info(json.loads(stdout))

await brd.activate()

await brd.update() await brd.update()


return brd return brd
@@ -615,9 +637,10 @@ class TestBiteLab(unittest.IsolatedAsyncioTestCase):
# and that the error got logged # and that the error got logged
le.assert_called_with('release script failure: board: \'cora-1\', ret: 1, stderr: b\'error\'') le.assert_called_with('release script failure: board: \'cora-1\', ret: 1, stderr: b\'error\'')


@patch('bitelab.BoardImpl.activate')
@patch('asyncio.create_subprocess_exec') @patch('asyncio.create_subprocess_exec')
@patch('bitelab.snmp.snmpget') @patch('bitelab.snmp.snmpget')
async def test_board_reserve_release(self, sg, cse):
async def test_board_reserve_release(self, sg, cse, biact):
# that when releasing a board that is not yet reserved # that when releasing a board that is not yet reserved
res = await self.client.post('/board/cora-1/release', res = await self.client.post('/board/cora-1/release',
auth=BiteAuth('anotherlongapikey')) auth=BiteAuth('anotherlongapikey'))
@@ -685,6 +708,9 @@ class TestBiteLab(unittest.IsolatedAsyncioTestCase):
cse.assert_called_with(self.settings.setup_script, 'reserve', cse.assert_called_with(self.settings.setup_script, 'reserve',
'cora-1', 'foo', 'pubsshkey', stdout=subprocess.PIPE, stderr=subprocess.PIPE) 'cora-1', 'foo', 'pubsshkey', stdout=subprocess.PIPE, stderr=subprocess.PIPE)


# and that the board was activated
biact.assert_called()

# that another user reserving the board # that another user reserving the board
res = await self.client.post('/board/cora-1/reserve', res = await self.client.post('/board/cora-1/reserve',
auth=BiteAuth('anotherlongapikey')) auth=BiteAuth('anotherlongapikey'))
@@ -875,8 +901,28 @@ class TestBiteLab(unittest.IsolatedAsyncioTestCase):
} }
self.assertEqual(res.json(), info) self.assertEqual(res.json(), info)


class TestBoardImpl(unittest.IsolatedAsyncioTestCase):
async def test_activate(self):
# that a board impl
opt = create_autospec(Attribute)
brd = BoardImpl('foo', 'bar', [ opt ])

await brd.activate()

opt.activate.assert_called_with(brd)

async def test_deactivate(self):
# that a board impl
opt = create_autospec(Attribute)
brd = BoardImpl('foo', 'bar', [ opt ])

await brd.deactivate()

opt.deactivate.assert_called_with(brd)

class TestAttrs(unittest.IsolatedAsyncioTestCase): class TestAttrs(unittest.IsolatedAsyncioTestCase):
async def test_serialconsole(self):
@patch('asyncio.create_subprocess_exec')
async def test_serialconsole(self, cse):
data = 'somepath' data = 'somepath'
sc = SerialConsole(data) sc = SerialConsole(data)


@@ -886,3 +932,22 @@ class TestAttrs(unittest.IsolatedAsyncioTestCase):


with self.assertRaises(TypeError): with self.assertRaises(TypeError):
await sc.setvalue(data) await sc.setvalue(data)

devfspath = 'eifd'

brd = BoardImpl('foo', 'bar', [ sc ])
brd.add_info(dict(devfspath=devfspath))

wrap_subprocess_exec(cse, retcode=0)

await sc.activate(brd)

cse.assert_called_with('devfs', '-m', devfspath, 'rule',
'apply', 'path', os.path.basename(await sc.getvalue()),
'unhide',
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)

wrap_subprocess_exec(cse, retcode=1)
with self.assertRaises(RuntimeError):
await sc.activate(brd)

+ 18
- 0
bitelab/abstract.py View File

@@ -36,11 +36,29 @@ class Attribute:
defattrname = None defattrname = None


async def getvalue(self): # pragma: no cover async def getvalue(self): # pragma: no cover
'''Get the value for this attribute.'''

raise NotImplementedError raise NotImplementedError


async def setvalue(self, v): # pragma: no cover async def setvalue(self, v): # pragma: no cover
'''Set the value for this attribute to v.'''

raise NotImplementedError raise NotImplementedError


async def activate(self, brd): # pragma: no cover
'''This activates the attribute on brd. brd is an
isntance of BoardImpl. This is called after the jail and
other resources have been allocated.'''

pass

async def deactivate(self, brd): # pragma: no cover
'''This deactivates the attribute on brd. brd is an
isntance of BoardImpl. This is called just before the jail
other resources will be deallocated.'''

pass

class ROAttribute(Attribute): class ROAttribute(Attribute):
'''A read-only attribute class. This implements setvalue, but '''A read-only attribute class. This implements setvalue, but
raises a TypeError when setvalue is called.''' raises a TypeError when setvalue is called.'''


+ 1
- 1
bitelab/testing.py View File

@@ -30,5 +30,5 @@


from .snmp import TestSNMPPower, TestSNMPWrapper from .snmp import TestSNMPPower, TestSNMPWrapper
from .data import TestDatabase from .data import TestDatabase
from . import TestBiteLab, TestUnhashLRU, TestAttrs
from . import TestBiteLab, TestUnhashLRU, TestAttrs, TestBoardImpl
from .__main__ import TestClient from .__main__ import TestClient

Loading…
Cancel
Save