diff --git a/bitelab/__main__.py b/bitelab/__main__.py index c291be4..827bfee 100644 --- a/bitelab/__main__.py +++ b/bitelab/__main__.py @@ -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):