diff --git a/termius/porting/commands.py b/termius/porting/commands.py index 09d108b..06931f4 100644 --- a/termius/porting/commands.py +++ b/termius/porting/commands.py @@ -74,7 +74,7 @@ def take_action(self, parsed_args): ) provider.import_hosts() - self.log.info('Skipped: %i' % len(provider.skipped_hosts)) + self.log.info('Skipped hosts %i' % len(provider.skipped_hosts)) self.log.info('SecureCRT hosts has been successfully imported.') def get_parser(self, prog_name): diff --git a/termius/porting/providers/base.py b/termius/porting/providers/base.py index 889cd95..2e723f33 100644 --- a/termius/porting/providers/base.py +++ b/termius/porting/providers/base.py @@ -31,7 +31,7 @@ def import_hosts(self): with self.storage: for host in hosts_to_import: - if not self.get_existed_host(host): + if not self.is_host_exists(host): self.storage.save(host) else: self.skipped_hosts.append(host.label) @@ -44,10 +44,15 @@ def assign_ssh_key_ids(self, new_ssh_key): new_ssh_key.id = existed_key.id return new_ssh_key - def get_existed_host(self, new_host): + def is_host_exists(self, new_host): """Retrieve exited host for new host.""" existed_hosts = self.storage.filter(Host, label=new_host.label) - return existed_hosts and existed_hosts[0] + for host in existed_hosts: + if host.group and new_host.group: + if host.group.label == new_host.group.label: + return True + + return False def get_existed_key(self, new_ssh_key): """Retrieve exited key for new key.""" diff --git a/termius/porting/providers/securecrt/parser.py b/termius/porting/providers/securecrt/parser.py index 08ac82e..661687f 100644 --- a/termius/porting/providers/securecrt/parser.py +++ b/termius/porting/providers/securecrt/parser.py @@ -1,47 +1,61 @@ # -*- coding: utf-8 -*- """Module with SecureCRT parser.""" +import collections from os.path import expanduser - class SecureCRTConfigParser(object): """SecureCRT xml parser.""" meta_sessions = ['Default'] - @classmethod - def parse_hosts(cls, xml): + def __init__(self, xml): + self.xml = xml + self.tree = {} + + def parse_hosts(self): """Parse SecureCRT Sessions.""" - sessions = cls.get_element_by_name( - xml.getchildren(), 'Sessions' + sessions = self.get_element_by_name( + self.xml.getchildren(), 'Sessions' ).getchildren() - parsed_hosts = [] - - for session in sessions: - if session.get('name') not in cls.meta_sessions: - host = cls.make_host(session) - if not host: - continue - - parsed_hosts.append(host) + self.parse_sessions(sessions, self.tree) - return parsed_hosts + return self.tree - @classmethod - def parse_identity(cls, xml): + def parse_sessions(self, sessions, parent_node): + for session in sessions: + if session.get('name') not in self.meta_sessions: + if not self.is_session_group(session): + host = self.make_host(session) + if not host: + continue + parent_node[host['label']] = host + else: + parent_node[session.get('name')] = {'group': True} + self.parse_sessions( + session.getchildren(), + parent_node[session.get('name')] + ) + + def is_session_group(self, session): + return self.get_element_by_name( + session.getchildren(), 'Hostname' + ) is None + + def parse_identity(self): """Parse SecureCRT SSH2 raw key.""" - identity = cls.get_element_by_name( - xml.getchildren(), 'SSH2' + identity = self.get_element_by_name( + self.xml.getchildren(), 'SSH2' ) if identity is None: return None - identity_filename = cls.get_element_by_name( + identity_filename = self.get_element_by_name( identity.getchildren(), 'Identity Filename V2' ) - if not cls.check_attribute(identity_filename): + if not self.check_attribute(identity_filename): return None path = identity_filename.text.split('/') @@ -59,33 +73,30 @@ def parse_identity(cls, xml): return private_key_path, public_key_path - @classmethod - def make_host(cls, session): + def make_host(self, session): """Adapt SecureCRT Session to Termius host.""" session_attrs = session.getchildren() - hostname = cls.get_element_by_name(session_attrs, 'Hostname') - port = cls.get_element_by_name(session_attrs, '[SSH2] Port') - username = cls.get_element_by_name(session_attrs, 'Username') + hostname = self.get_element_by_name(session_attrs, 'Hostname') + port = self.get_element_by_name(session_attrs, '[SSH2] Port') + username = self.get_element_by_name(session_attrs, 'Username') - if not cls.check_attribute(hostname): + if not self.check_attribute(hostname): return None return { 'label': session.get('name'), 'hostname': hostname.text, - 'port': port.text if cls.check_attribute(port) else '22', + 'port': port.text if self.check_attribute(port) else '22', 'username': username.text - if cls.check_attribute(username) else None + if self.check_attribute(username) else None } - @classmethod - def check_attribute(cls, attr): + def check_attribute(self, attr): """Check an attribute.""" return attr is not None and attr.text - @classmethod - def get_element_by_name(cls, elements, name): + def get_element_by_name(self, elements, name): """Get SecureCRT config block.""" for element in elements: if element.get('name') == name: diff --git a/termius/porting/providers/securecrt/provider.py b/termius/porting/providers/securecrt/provider.py index 9c2db81..160d67a 100644 --- a/termius/porting/providers/securecrt/provider.py +++ b/termius/porting/providers/securecrt/provider.py @@ -19,7 +19,8 @@ def __init__(self, source, *args, **kwargs): """Contruct new service to sync ssh config.""" super(SecureCRTPortingProvider, self).__init__(*args, **kwargs) - self.config_source = source + xml_root = ElementTree.parse(source).getroot() + self.parser = SecureCRTConfigParser(xml_root) def export_hosts(self): """Skip export.""" @@ -27,70 +28,77 @@ def export_hosts(self): def provider_hosts(self): """Retrieve host instances from ssh config.""" - root = ElementTree.parse(self.config_source).getroot() - self.logger.info('Got SecureCRT xml root...') - hosts = [] - self.logger.info('Parse SecureCRT hosts...') - raw_hosts = SecureCRTConfigParser.parse_hosts( - root - ) - self.logger.info('Parsed %i entries.' % len(raw_hosts)) - identity_paths = SecureCRTConfigParser.parse_identity(root) - main_group = Group(label='SecureCRT') + result_hosts = [] + tree = self.parser.parse_hosts() - group_config = SshConfig( - identity=Identity( - is_visible=False, - label='SecureCRT' - ) - ) + root_group = Group(label='SecureCRT') + identity_paths = self.parser.parse_identity() if identity_paths: self.logger.info('Found private key path: %s' % identity_paths[0]) self.logger.info('Found public key path: %s' % identity_paths[1]) try: - with open(identity_paths[0], 'r') as private_key_file: - private_key = private_key_file.read() - - with open(identity_paths[1], 'r') as public_key_file: - public_key = public_key_file.read() - - key = SshKey( - label='SecureCRT', - private_key=private_key, - public_key=public_key + key = self.create_key(identity_paths) + root_group.ssh_config = SshConfig( + identity=Identity( + ssh_key=key, + label='SecureCRT', + is_visible=False + ) ) - group_config.identity.ssh_key = key except IOError: self.logger.info( 'Warning: cannot import SSH2 raw key %s' % identity_paths[1] ) - main_group.ssh_config = group_config + self.create_entries_from_tree(tree, result_hosts, root_group) + self.logger.info('Parsed hosts %i' % len(result_hosts)) + return result_hosts - for raw_host in raw_hosts: - host = Host( - label=raw_host['label'], - address=raw_host['hostname'] - ) - host.group = main_group - ssh_config = SshConfig( - port=raw_host['port'] - ) + def create_entries_from_tree(self, tree, result_hosts, parent_group=None): + for label, node in tree.iteritems(): + if not isinstance(node, dict): + continue - if raw_host['username']: - identity = Identity( - username=raw_host.get('username'), - is_visible=False, - label=raw_host.get('username') + if not node.get('group', None): + result_hosts.append( + self.create_host(node, parent_group) ) + else: + group = Group(label=label, parent_group=parent_group) + self.create_entries_from_tree(node, result_hosts, group) + + def create_host(self, raw_host, group): + host = Host( + label=raw_host['label'], + address=raw_host['hostname'], + group=group + ) + host.ssh_config = SshConfig( + port=raw_host['port'] + ) - ssh_config.identity = identity + if raw_host['username']: + identity = Identity( + username=raw_host.get('username'), + is_visible=False, + label=raw_host.get('username') + ) - host.ssh_config = ssh_config + host.ssh_config.identity = identity - hosts.append(host) + return host - self.logger.info('Adapted %i entries.' % len(raw_hosts)) - return hosts + def create_key(self, identity_paths): + with open(identity_paths[0], 'r') as private_key_file: + private_key = private_key_file.read() + + with open(identity_paths[1], 'r') as public_key_file: + public_key = public_key_file.read() + + return SshKey( + label='SecureCRT', + private_key=private_key, + public_key=public_key + )