| @@ -35,7 +35,10 @@ from unittest.mock import patch, AsyncMock, Mock | |||
| from . import BiteAuth, Board | |||
| import argparse | |||
| import asyncio | |||
| import contextlib | |||
| import io | |||
| import os | |||
| import sys | |||
| import unittest | |||
| @@ -78,14 +81,35 @@ def output_board(brd): | |||
| for i in sorted(brd.attrs): | |||
| print('\t%s\t%s' % (i, repr(brd.attrs[i]))) | |||
| def get_sshpubkey(fname): | |||
| raise OSError | |||
| async def real_main(): | |||
| baseurl = os.environ['BITELAB_URL'] | |||
| authkey = os.environ['BITELAB_AUTH'] | |||
| client = AsyncClient(base_url=baseurl) | |||
| parser = argparse.ArgumentParser() | |||
| subparsers = parser.add_subparsers(title='subcommands', | |||
| dest='subparser_name', | |||
| description='valid subcommands', help='additional help') | |||
| parse_list = subparsers.add_parser('list', help='list available board classes') | |||
| parser_reserve = subparsers.add_parser('reserve', aliases=[ 'release' ], help='reserve/release a board') | |||
| parser_reserve.add_argument('-i', metavar='identity_file', type=str, help='file name for ssh public key') | |||
| parser_reserve.add_argument('board', type=str, help='name of the board or class') | |||
| parser_set = subparsers.add_parser('set', help='set attributes on a board') | |||
| parser_set.add_argument('setvars', type=str, nargs='+', help='name of the board or class') | |||
| parser_set.add_argument('board', type=str, help='name of the board or class') | |||
| args = parser.parse_args() | |||
| #print(repr(args), file=sys.stderr) | |||
| try: | |||
| if sys.argv[1] == 'list': | |||
| if args.subparser_name == 'list': | |||
| res = await client.get('board/classes', | |||
| auth=BiteAuth(authkey), **_httpxargs) | |||
| @@ -96,18 +120,21 @@ async def real_main(): | |||
| print('\t' + i) | |||
| res.close() | |||
| elif sys.argv[1] in ('reserve', 'release'): | |||
| elif args.subparser_name in ('reserve', 'release'): | |||
| kwargs = _httpxargs.copy() | |||
| with contextlib.suppress(OSError): | |||
| kwargs['json'] = dict(sshpubkey=get_sshpubkey(args.i)) | |||
| res = await client.post('board/%s/%s' % | |||
| (urllib.parse.quote(sys.argv[2], safe=''), | |||
| sys.argv[1]), | |||
| auth=BiteAuth(authkey), **_httpxargs) | |||
| (urllib.parse.quote(args.board, safe=''), | |||
| args.subparser_name), | |||
| auth=BiteAuth(authkey), **kwargs) | |||
| check_res_code(res) | |||
| brd = Board.parse_obj(res.json()) | |||
| output_board(brd) | |||
| elif sys.argv[1] == 'set': | |||
| elif args.subparser_name == 'set': | |||
| board_id = sys.argv[-1] | |||
| res = await client.post('board/%s/attrs' % | |||
| urllib.parse.quote(board_id, safe=''), | |||
| @@ -202,8 +229,44 @@ class TestClient(unittest.TestCase): | |||
| # XXX - add error cases for UI | |||
| @patch('bitelab.__main__.get_sshpubkey') | |||
| @patch.dict(sys.__dict__, dict(argv=[ '', 'reserve', 'cora-z7s' ])) | |||
| def test_reserve(self): | |||
| def test_reserve(self, gspk): | |||
| gspk.side_effect = OSError() | |||
| ac = self.ac | |||
| acp = self.acp | |||
| acp.return_value.status_code = HTTP_200_OK | |||
| acp.return_value.json.return_value = Board(name='cora-1', | |||
| brdclass='cora-z7s', reserved=True, | |||
| attrs={ | |||
| 'ip': '172.20.20.5', | |||
| 'power': False, | |||
| }).dict() | |||
| keydata = 'randomkeydata' | |||
| ret, stdout = self.runMain() | |||
| output = '''Name:\tcora-1 | |||
| Class:\tcora-z7s | |||
| Attributes: | |||
| \tip\t'172.20.20.5' | |||
| \tpower\tFalse | |||
| ''' | |||
| self.assertEqual(ret, 0) | |||
| self.assertEqual(stdout, output) | |||
| ac.assert_called_with(base_url='http://someserver/') | |||
| #args = { 'sshpubkey': keydata } | |||
| acp.assert_called_with('board/cora-z7s/reserve', | |||
| #json=args, | |||
| auth=BiteAuth('thisisanapikey'), **_httpxargs) | |||
| @patch('bitelab.__main__.get_sshpubkey') | |||
| @patch.dict(sys.__dict__, dict(argv=[ '', 'reserve', '-i', 'fixtures/testsshkey.pub', 'cora-z7s' ])) | |||
| def test_reserve_ssh(self, gspk): | |||
| ac = self.ac | |||
| acp = self.acp | |||
| acp.return_value.status_code = HTTP_200_OK | |||
| @@ -214,6 +277,9 @@ class TestClient(unittest.TestCase): | |||
| 'power': False, | |||
| }).dict() | |||
| keydata = 'keydata' | |||
| gspk.return_value = keydata | |||
| ret, stdout = self.runMain() | |||
| output = '''Name:\tcora-1 | |||
| @@ -228,7 +294,9 @@ Attributes: | |||
| ac.assert_called_with(base_url='http://someserver/') | |||
| acp.assert_called_with('board/cora-z7s/reserve', auth=BiteAuth('thisisanapikey'), **_httpxargs) | |||
| acp.assert_called_with('board/cora-z7s/reserve', | |||
| json=dict(sshpubkey=keydata), | |||
| auth=BiteAuth('thisisanapikey'), **_httpxargs) | |||
| @patch.dict(sys.__dict__, dict(argv=[ '', 'release', 'cora-z7s' ])) | |||
| def test_release(self): | |||
| @@ -257,7 +325,8 @@ Attributes: | |||
| acp.assert_called_with('board/cora-z7s/release', | |||
| auth=BiteAuth('thisisanapikey'), **_httpxargs) | |||
| @patch.dict(sys.__dict__, dict(argv=[ '', 'set', 'power=on', 'cora-z7s' ])) | |||
| @patch('bitelab.__main__._typeconv', dict(power=makebool, other=makebool)) | |||
| @patch.dict(sys.__dict__, dict(argv=[ '', 'set', 'power=on', 'other=off', 'cora-z7s' ])) | |||
| def test_set(self): | |||
| ac = self.ac | |||
| acp = self.acp | |||
| @@ -284,7 +353,7 @@ Attributes: | |||
| ac.assert_called_with(base_url='http://someserver/') | |||
| acp.assert_called_with('board/cora-z7s/attrs', | |||
| auth=BiteAuth('thisisanapikey'), json=dict(power=True), | |||
| auth=BiteAuth('thisisanapikey'), json=dict(power=True, other=False), | |||
| **_httpxargs) | |||
| def test_make_attrs(self): | |||