| @@ -35,7 +35,10 @@ from unittest.mock import patch, AsyncMock, Mock | |||||
| from . import BiteAuth, Board | from . import BiteAuth, Board | ||||
| import argparse | |||||
| import asyncio | import asyncio | ||||
| import contextlib | |||||
| import io | |||||
| import os | import os | ||||
| import sys | import sys | ||||
| import unittest | import unittest | ||||
| @@ -78,14 +81,35 @@ def output_board(brd): | |||||
| for i in sorted(brd.attrs): | for i in sorted(brd.attrs): | ||||
| print('\t%s\t%s' % (i, repr(brd.attrs[i]))) | print('\t%s\t%s' % (i, repr(brd.attrs[i]))) | ||||
| def get_sshpubkey(fname): | |||||
| raise OSError | |||||
| async def real_main(): | async def real_main(): | ||||
| baseurl = os.environ['BITELAB_URL'] | baseurl = os.environ['BITELAB_URL'] | ||||
| authkey = os.environ['BITELAB_AUTH'] | authkey = os.environ['BITELAB_AUTH'] | ||||
| client = AsyncClient(base_url=baseurl) | 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: | try: | ||||
| if sys.argv[1] == 'list': | |||||
| if args.subparser_name == 'list': | |||||
| res = await client.get('board/classes', | res = await client.get('board/classes', | ||||
| auth=BiteAuth(authkey), **_httpxargs) | auth=BiteAuth(authkey), **_httpxargs) | ||||
| @@ -96,18 +120,21 @@ async def real_main(): | |||||
| print('\t' + i) | print('\t' + i) | ||||
| res.close() | 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' % | 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) | check_res_code(res) | ||||
| brd = Board.parse_obj(res.json()) | brd = Board.parse_obj(res.json()) | ||||
| output_board(brd) | output_board(brd) | ||||
| elif sys.argv[1] == 'set': | |||||
| elif args.subparser_name == 'set': | |||||
| board_id = sys.argv[-1] | board_id = sys.argv[-1] | ||||
| res = await client.post('board/%s/attrs' % | res = await client.post('board/%s/attrs' % | ||||
| urllib.parse.quote(board_id, safe=''), | urllib.parse.quote(board_id, safe=''), | ||||
| @@ -202,8 +229,44 @@ class TestClient(unittest.TestCase): | |||||
| # XXX - add error cases for UI | # XXX - add error cases for UI | ||||
| @patch('bitelab.__main__.get_sshpubkey') | |||||
| @patch.dict(sys.__dict__, dict(argv=[ '', 'reserve', 'cora-z7s' ])) | @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 | ac = self.ac | ||||
| acp = self.acp | acp = self.acp | ||||
| acp.return_value.status_code = HTTP_200_OK | acp.return_value.status_code = HTTP_200_OK | ||||
| @@ -214,6 +277,9 @@ class TestClient(unittest.TestCase): | |||||
| 'power': False, | 'power': False, | ||||
| }).dict() | }).dict() | ||||
| keydata = 'keydata' | |||||
| gspk.return_value = keydata | |||||
| ret, stdout = self.runMain() | ret, stdout = self.runMain() | ||||
| output = '''Name:\tcora-1 | output = '''Name:\tcora-1 | ||||
| @@ -228,7 +294,9 @@ Attributes: | |||||
| ac.assert_called_with(base_url='http://someserver/') | 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' ])) | @patch.dict(sys.__dict__, dict(argv=[ '', 'release', 'cora-z7s' ])) | ||||
| def test_release(self): | def test_release(self): | ||||
| @@ -257,7 +325,8 @@ Attributes: | |||||
| acp.assert_called_with('board/cora-z7s/release', | acp.assert_called_with('board/cora-z7s/release', | ||||
| auth=BiteAuth('thisisanapikey'), **_httpxargs) | 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): | def test_set(self): | ||||
| ac = self.ac | ac = self.ac | ||||
| acp = self.acp | acp = self.acp | ||||
| @@ -284,7 +353,7 @@ Attributes: | |||||
| ac.assert_called_with(base_url='http://someserver/') | ac.assert_called_with(base_url='http://someserver/') | ||||
| acp.assert_called_with('board/cora-z7s/attrs', | 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) | **_httpxargs) | ||||
| def test_make_attrs(self): | def test_make_attrs(self): | ||||