From 81ee2f433260eb0705aafe758687a963922e71a7 Mon Sep 17 00:00:00 2001 From: Maxbey Date: Tue, 5 Sep 2017 17:47:31 +0600 Subject: [PATCH] Add tests for SecureCRT groups parsing --- .travis.yml | 1 - termius/porting/providers/securecrt/parser.py | 7 +- .../porting/providers/securecrt/provider.py | 16 +- .../unit/porting/providers/securecrt_test.py | 161 ++++++++++++++++++ 4 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 tests/unit/porting/providers/securecrt_test.py diff --git a/.travis.yml b/.travis.yml index c3a81b4..515d349 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ python: - "2.7" - "3.4" - "3.5" - - "3.5-dev" # 3.5 development branch install: - git clone https://github.com/sstephenson/bats.git && cd bats && ./install.sh .. && cd - - sudo apt-get install pandoc diff --git a/termius/porting/providers/securecrt/parser.py b/termius/porting/providers/securecrt/parser.py index 661687f..962c309 100644 --- a/termius/porting/providers/securecrt/parser.py +++ b/termius/porting/providers/securecrt/parser.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- """Module with SecureCRT parser.""" -import collections from os.path import expanduser + class SecureCRTConfigParser(object): """SecureCRT xml parser.""" meta_sessions = ['Default'] def __init__(self, xml): + """Construct parser instance.""" self.xml = xml self.tree = {} @@ -23,6 +24,7 @@ def parse_hosts(self): return self.tree def parse_sessions(self, sessions, parent_node): + """Parse SecureCRT sessions.""" for session in sessions: if session.get('name') not in self.meta_sessions: if not self.is_session_group(session): @@ -31,13 +33,14 @@ def parse_sessions(self, sessions, parent_node): continue parent_node[host['label']] = host else: - parent_node[session.get('name')] = {'group': True} + parent_node[session.get('name')] = {'__group': True} self.parse_sessions( session.getchildren(), parent_node[session.get('name')] ) def is_session_group(self, session): + """Check node element type""" return self.get_element_by_name( session.getchildren(), 'Hostname' ) is None diff --git a/termius/porting/providers/securecrt/provider.py b/termius/porting/providers/securecrt/provider.py index 160d67a..a2528ce 100644 --- a/termius/porting/providers/securecrt/provider.py +++ b/termius/porting/providers/securecrt/provider.py @@ -16,7 +16,7 @@ class SecureCRTPortingProvider(BasePortingProvider): logger = logging.getLogger(__name__) def __init__(self, source, *args, **kwargs): - """Contruct new service to sync ssh config.""" + """Construct provider instance.""" super(SecureCRTPortingProvider, self).__init__(*args, **kwargs) xml_root = ElementTree.parse(source).getroot() @@ -27,7 +27,7 @@ def export_hosts(self): pass def provider_hosts(self): - """Retrieve host instances from ssh config.""" + """Retrieve SecureCRT sessions from xml config.""" result_hosts = [] tree = self.parser.parse_hosts() @@ -54,14 +54,16 @@ def provider_hosts(self): self.create_entries_from_tree(tree, result_hosts, root_group) self.logger.info('Parsed hosts %i' % len(result_hosts)) + self.logger.info('Importing...') return result_hosts def create_entries_from_tree(self, tree, result_hosts, parent_group=None): - for label, node in tree.iteritems(): + """Create instances from groups tree.""" + for label, node in tree.items(): if not isinstance(node, dict): continue - if not node.get('group', None): + if not node.get('__group', None): result_hosts.append( self.create_host(node, parent_group) ) @@ -70,6 +72,7 @@ def create_entries_from_tree(self, tree, result_hosts, parent_group=None): self.create_entries_from_tree(node, result_hosts, group) def create_host(self, raw_host, group): + """Create instances from groups tree.""" host = Host( label=raw_host['label'], address=raw_host['hostname'], @@ -91,10 +94,11 @@ def create_host(self, raw_host, group): return host def create_key(self, identity_paths): - with open(identity_paths[0], 'r') as private_key_file: + """Create ssh key instance.""" + with open(identity_paths[0], 'rb') as private_key_file: private_key = private_key_file.read() - with open(identity_paths[1], 'r') as public_key_file: + with open(identity_paths[1], 'rb') as public_key_file: public_key = public_key_file.read() return SshKey( diff --git a/tests/unit/porting/providers/securecrt_test.py b/tests/unit/porting/providers/securecrt_test.py new file mode 100644 index 0000000..0d05e52 --- /dev/null +++ b/tests/unit/porting/providers/securecrt_test.py @@ -0,0 +1,161 @@ +from io import BytesIO +from unittest import TestCase + +from mock import patch, mock_open, call + +from termius.core.models.terminal import Host, Group, SshConfig, Identity, \ + SshKey +from termius.porting.providers.securecrt.provider import\ + SecureCRTPortingProvider + + +class SecureCRTProviderTest(TestCase): + def test_tree_parsing(self): + securecrt_config = """ + + + + 0 + addr0 + user0 + + + + + 1 + addr1 + user1 + + + + + 2 + addr2 + user2 + + + serial_user + + + + + + """ + xml_path = '/some/path/securecrt.xml' + root_group = Group(label='SecureCRT') + hosts_folder = Group( + label='hosts', parent_group=root_group + ) + expected = [ + Host( + label='host2', + address='addr2', + group=Group(label='folder1', parent_group=hosts_folder), + ssh_config=SshConfig( + port='2', + identity=Identity( + label='user2', username='user2', is_visible=False + ) + ) + ), + Host( + label='host1', + address='addr1', + group=Group(label='folder0', parent_group=hosts_folder), + ssh_config=SshConfig( + port='1', + identity=Identity( + label='user1', username='user1', is_visible=False + ) + ) + ), + Host( + label='host0', address='addr0', group=root_group, + ssh_config=SshConfig( + port='0', + identity=Identity( + label='user0', username='user0', is_visible=False + ) + ) + ) + ] + + with patch('xml.etree.ElementTree.open', + mock_open(read_data=securecrt_config)) as mocked_xml: + service = SecureCRTPortingProvider(xml_path, None, '') + hosts = service.provider_hosts() + mocked_xml.assert_called_once_with(xml_path, 'rb') + self.assertEquals( + self.sort_hosts(hosts), + self.sort_hosts(expected) + ) + + def test_ssh2_identity_parsing(self): + public_key = b'public' + private_key = b'private' + xml_path = '/some/path/securecrt.xml' + + root_group = Group( + label='SecureCRT', + ssh_config=SshConfig( + identity=Identity( + label='SecureCRT', + is_visible=False, + ssh_key=SshKey( + label='SecureCRT', + private_key=private_key, + public_key=public_key + ) + ) + ) + ) + securecrt_config = """ + + + + 0 + addr0 + user0 + + + + /Users/termius/folder/key.pub::rawkey + + + """ + expected = [ + Host( + label='host0', group=root_group, address='addr0', + ssh_config=SshConfig(port='0', + identity=Identity( + label='user0', + is_visible=False, + username='user0' + ) + ) + ) + ] + expected_calls = [ + call('/Users/termius/folder/key', 'rb'), + call('/Users/termius/folder/key.pub', 'rb') + ] + + with patch('xml.etree.ElementTree.open', + mock_open(read_data=securecrt_config)) as mocked_xml: + + with patch('termius.porting.providers.securecrt.provider.open', + mock_open(read_data='')) as mocked_open: + service = SecureCRTPortingProvider(xml_path, None, '') + mocked_open.side_effect = [ + BytesIO(private_key), BytesIO(public_key) + ] + hosts = service.provider_hosts() + self.assertEquals( + self.sort_hosts(hosts), + self.sort_hosts(expected) + ) + mocked_xml.assert_called_once_with(xml_path, 'rb') + mocked_open.assert_has_calls(expected_calls) + + def sort_hosts(self, hosts): + return sorted(hosts, key=lambda host: host['address']) \ No newline at end of file