def _va_parse_microsegment_epi(self, output=None): """ helper method to parse 'show microsegment epi' or 'show micro-segmentation epi uuid' returns (list): look at the following output [Micro_Epi(EPI='564DDF39-5C53-66E4-4F64-6CB7B2B8BC9B', EPI_hostname='-', segment='100', micro_vlan='1'), Micro_Epi(EPI='564DDF39-5C53-66E4-4F64-6CB7B2B8BC9B', EPI_hostname='-', segment='100', micro_vlan='2'), Micro_Epi(EPI='564DDF39-5C53-66E4-4F64-6CB7B2B8BC9B', EPI_hostname='-', segment='200', micro_vlan='3'), Micro_Epi(EPI='564DDF39-5C53-66E4-4F64-6CB7B2B8BC9B', EPI_hostname='-', segment='200', micro_vlan='4'), Micro_Epi(EPI='564D6404-88C7-0A2C-04D2-3CD6F05E3C6D', EPI_hostname='-', segment='100', micro_vlan='1')] """ parsed = list() parse_info = va_parse_as_lines(output) parse_info.pop(-1) micro_epi = namedtuple( 'Micro_Epi', ['EPI', 'EPI_hostname', 'segment', 'micro_vlan']) for line in parse_info: line = line.strip() if line.startswith('EPI') or line.startswith( '---') or 'show micro-segmentation' in line: continue else: parse_values = list() values = line.split('|') for list_element in values: parse_values.append(list_element.strip()) parsed.append(micro_epi(*parse_values)) return parsed
def _va_parse_interface_mapping(self, output=None): """ helper method to parse interface mapping [ Config(varmour_intf='xe-1/0/0', none='--->', vm_intf='eth0', mac='00:0c:29:5a:62:e9', vm_index='6') ] """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) parsed = list() Config = namedtuple( 'Config', ['varmour_intf', 'none', 'vm_intf', 'mac', 'vm_index']) for line in va_parse_as_lines(output): line = line.lstrip() if line.startswith('xe-'): values = line.split() try: parsed.append(Config(*values)) except TypeError: pass return parsed
def _va_parse_epi_interface(self, output=None): """ Helpter method to parse interface of epi show chassis epi 2-6 interface NAME IP MAC ID STATE(ADMIN/LINK) ----------------------------------------------------------------------------- fabric-intf 10.0.0.96 0:c:29:d9:5:eb 0x600 Up/Up mgt-intf 10.11.120.96 0:c:29:d9:5:f5 0x601 Up/Up north-intf 0.0.0.0 0:c:29:d9:5:ff 0x602 Up/Up south-intf 0.0.0.0 0:c:29:d9:5:9 0x603 Up/Up ##: disabled due to user request. Use command "request chassis epi ..." to bring the interface up. return (List): a list like this [ Intf(NAME='fabric-intf', IP='10.0.0.96', MAC='0:c:29:d9:5:eb', ID='0x600', STATE='Up/Up'), Intf(NAME='mgt-intf', IP='10.11.120.96', MAC='0:c:29:d9:5:f5', ID='0x601', STATE='Up/Up'), Intf(NAME='north-intf', IP='0.0.0.0', MAC='0:c:29:d9:5:ff', ID='0x602', STATE='Up/Up'), Intf(NAME='south-intf', IP='0.0.0.0', MAC='0:c:29:d9:5:9', ID='0x603', STATE='Up/Up') ] """ parsed = list() Intf = namedtuple('Intf', ['NAME', 'IP', 'MAC', 'ID', 'STATE']) for line in va_parse_as_lines(output): line = line.lstrip() if not line.startswith('NAME')\ and not line.startswith('-')\ and not line.startswith('##')\ and not line.startswith('varmour'): values = line.split() print(values) parsed.append(Intf(*values)) return parsed
def _va_parse_interfaces(self, output=None): """ parse the output of 'show interface' command and return the output as a dict, with the attributes as keys. """ parsed = list() for line in va_parse_as_lines(output): line = line.strip() if not line.startswith('name') and\ not line.startswith('-'): parsed.append(tuple(line.split())) return parsed
def _va_parse_zone(self, output=None): """ helper method to parse zone varmour@vArmour#ROOT(config)> do show zone ID Name Type Asymmetric-routing Interfaces -- ---- ---- ------------------ ---------- 1 Null NULL No 16 Intc Intc No 19 test L3 No xe-1/0/3.1 xe-1/0/3.3 xe-1/0/3.2 21 example L2 No varmour@vArmour#ROOT(config)> return (dict): look at the following output [ Zone(ID='1', Name='Null', Type='NULL', Asymmetric_routing='No', Interfaces=''), Zone(ID='16', Name='Intc', Type='Intc', Asymmetric_routing='No', Interfaces=''), Zone(ID='19', Name='test', Type='L3', Asymmetric_routing='No', Interfaces='xe-1/0/3.1, xe-1/0/3.3, xe-1/0/3.2'), Zone(ID='21', Name='example', Type='L2', Asymmetric_routing='No', Interfaces='') ] """ parsed = list() Zone = namedtuple( 'Zone', ['ID', 'Name', 'Type', 'Asymmetric_routing', 'Interfaces']) for line in va_parse_as_lines(output): line = line.lstrip() if not line.startswith('ID')\ and not line.startswith('-')\ and not line.startswith('varmour'): if not line.startswith('xe'): values = line.split() if len(values) == 4: values.append('') else: if line.startswith('xe'): parsed.remove(Zone(*values)) values[-1] += ',{}'.format(line) try: parsed.append(Zone(*values)) except TypeError: pass return parsed
def va_get_chassis_fabric_vxlan_info(self): """ get chassis fabric vxlan info param : None return (list) : look at the following output varmour@vArmour#ROOT> show chassis fabric Global VXLAN Tunnel ID: 10 DEV-ID | HOSTNAME | STATE | MGT-IP | FABRIC-IP | FABRIC-GW | MODE ------------------------------------------------------------------------------------------------------------------------- 1 | vArmour | Active | 10.11.120.41/24 | 10.0.0.41/24 | -- | DIR 2 | vArmour | In-Active | -- | 10.0.0.43/24 | -- | 3 | vArmour | Active | 10.11.120.46/24 | 10.0.0.46/24 | -- | CP 4 | vArmour | Active | 10.11.120.43/24 | 10.0.0.43/24 | -- | CP return (list) : [ 'Global VXLAN Tunnel ID: 10', fabric(DEVID='1', HOSTNAME='vArmour', STATE='Active', MGT_IP=' 10.11.120.41/24', FABRIC_IP=' 10.0.0.41/24', FABRIC_GW='--', MODE='DIR'), fabric(DEVID='2', HOSTNAME=' vArmour', STATE=' In-Active', MGT_IP='--', FABRIC_IP=' 10.0.0.43/24', FABRIC_GW='--', MODE=' '), fabric(DEVID='3', HOSTNAME=' vArmour', STATE=' Active', MGT_IP=' 10.11.120.46/24', FABRIC_IP=' 10.0.0.46/24', FABRIC_GW='--', MODE='CP'), fabric(DEVID='4', HOSTNAME=' vArmour', STATE=' Active', MGT_IP=' 10.11.120.43/24', FABRIC_IP=' 10.0.0.43/24 ', FABRIC_GW='--', MODE=' CP') ] example : dir_1.va_get_chassis_fabric_vxlan_info() """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) parsed = list() Usert = namedtuple('fabric',['DEVID','HOSTNAME','STATE','MGT_IP','FABRIC_IP','FABRIC_GW','MODE']) rt = self._access.va_cli("show chassis fabric") for line in va_parse_as_lines(rt): line = line.replace(' ','') if line.startswith('Global VXLAN Tunnel ID') and not line.startswith('DEV-ID') \ and 'show chassis fabric' not in line and not line.startswith('-'): parsed.append(line) elif not line.startswith('Global VXLAN Tunnel ID') and not line.startswith('DEV-ID') \ and not line.startswith('-') and 'show chassis fabric' not in line: values = line.split("|") if '-' in values: values = "_" try: parsed.append(Usert(*values)) except TypeError: pass logger.info(parsed) return parsed
def _va_parse_address(self, output=None): """ helper method to parse 'show address' kwargs: :output (str): unicode str output of the cli command returns (list): sample output below """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) parsed = list() for line in va_parse_as_lines(output): line = line.lstrip() if not line.startswith('Name'): parsed.append(line.split()) return parsed
def send_dns_query(self, name, server='localhost', *args, **kwargs): """ method to send query to dns server return: a tuple of name and ip address example: send_dns_query('varmour.com') """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) cmd = "dig @{} {}".format(server, name) output = self._access.shell(cmd) ip = None for line in va_parse_as_lines(output[0]): if name in line and re.search(r'((\d+\.){3}\d+)', line) is not None: try: fqdn, ttl, xx, type, ip = line.split() except ValueError: pass logger.info('Answer from DNS: {}, {}'.format(name, ip)) return(name, ip)
def _va_parse_address_group_member(self, output): """ Helper method to parse address-group member list Params: output : unicode str output of the cli command Return : list : list of parsed information ['saddr', 'daddr'] """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) parsed = list() for line in va_parse_as_lines(output): line = line.lstrip() if not line.startswith('Address Group') and re.search( r'\w+\@\w+\#\w+', line) is None: parsed.append(line) return parsed
def _va_parse_user_info(self, output=None): """ helper method to parse 'show system user name' param : optput = show system user info varmour@vArmour#ROOT> show system user ID ROLE VSYS -- ---- ---- varmour admin ROOT varmour_no_cli admin ROOT mine operator ROOT min reader ROOT Total users: 4 return (dict):look at the following output { 'mine': {'ID': 'mine', 'ROLE': 'operator', 'VSYS': 'ROOT'}, 'min': {'ID': 'min', 'ROLE': 'reader', 'VSYS': 'ROOT'}, 'varmour_no_cli': {'ID': 'varmour_no_cli', 'ROLE': 'admin', 'VSYS': 'ROOT'}, 'varmour': {'ID': 'varmour', 'ROLE': 'admin', 'VSYS': 'ROOT'} } example : _va_parse_user_info(output) """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) parsed = dict() for line in va_parse_as_lines(output): if not line.startswith('ID') and not line.startswith('-') and \ 'show system user' not in line and not line.startswith('Total users'): for key in line.split()[0:1]: parsed[key] = dict() parsed[key]['ID'] = line.split()[0:1] parsed[key]['ROLE'] = line.split()[1:2] parsed[key]['VAYS'] = line.split()[2:] logger.info(parsed) return parsed
def _va_parse_ha_status(self, output=None): """ helper method to parse 'show high-availabe' return (dict): look at the following output { 'HA Mode': 'OFF', 'IF IDX': 'eth2', 'HA Overlay(AWS)': 'Not Enabled', 'HA Failover State': 'Failover Not Ready', 'Preempt': 'Disable' 'HA STATUS': ['PRIMARY BACKUP 702989293 50 8.8.8.2 1', 'MASTER 691058960 50 8.8.8.1 (self) 2'] } """ parsed = dict() parsed['HA STATUS'] = list() for line in va_parse_as_lines(output): line = line.strip() if ':' in line: key, value = line.split(':') parsed[key.strip()] = value.strip() elif line.startswith('MASTER') or \ line.startswith('INIT') or \ line.startswith('INELIGIBLE'): values = tuple(line.split()) parsed['HA STATUS'].append(values) elif line.startswith('PRIMARY BACKUP'): values = list(line.split()) values[:2] = [' '.join(values[:2])] parsed['HA STATUS'].append(tuple(values)) else: continue return parsed
def _va_parse_interface(self, output=None): """ parse the output of 'show interface' command and return the output as a dict, with the attributes as keys. """ parsed = dict() for line in va_parse_as_lines(output): line = line.strip() if line.startswith('--'): attrib, value = line.split('\t') attrib = attrib.split(None, 1)[1] attrib = attrib.strip() value = value.strip() parsed[attrib] = value continue elif line.startswith('MTU'): props = line.split(',') for prop in props: attrib, value = prop.split(':') attrib = attrib.strip() value = value.strip() parsed[attrib] = value continue elif line.startswith('OSPF'): parsed['ospf'] = line else: try: line = line.strip() attrib, value = line.split(':', 1) attrib = attrib.strip() value = value.strip() parsed[attrib] = value except ValueError: continue return parsed
def va_get_debug_status(self, *args, **kwargs): """ API to get status of debug switch param : kwargs : dict example : va_get_debug_status(**kwargs) kwargs = { 'name' : name of debug switch, such as 'enable_sn_debug' } return: :string - 0/1 or a dict like this: {'enable_vadb_debug': '0', 'enable_io_debug': '0', 'enable_agent_debug': '0', 'enable_re_debug': '0', 'enable_konfd_debug': '0', 'enable_cn_debug': '0', 'enable_sn_debug': '0'} """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) setup_ini_file = '/config-varmour/configuration/varmour_conf/setup.ini' output = self._access.va_shell('cat {}'.format(setup_ini_file), exit=False) output_list = va_parse_as_lines(output) debug_status = dict() for info in output_list: if 'enable_' in info: debug_info = info.split('=') debug_status[debug_info[0]] = debug_info[1] if 'name' in kwargs: name = kwargs.get('name') if name in debug_status: debug_status = int(debug_status[name]) else: debug_status = 0 logger.info('Debug status: {}'.format(debug_status)) return debug_status
def config_dhcp(self, *args, **kwargs): """ method to configure dhcp server param : kwargs : dict server : dhcp server ip address, range : dhcp range ip address for client, gateway: gateway for client, subnet : subnet for client, Returns: :bool - True on success or False on failure: example: config_dhcp(server='2.2.2.10', range='2.2.2.2-2.2.2.6', gateway='2.2.2.1', subnet='2.2.2.0') """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) if 'range' not in kwargs: raise ValueError( 'The "range" is a mandatory parameter, should be split with "-"' ) range = kwargs.get('range') try: range = range.split('-') except: raise ValueError('The range value should be split with "-"!') range_start = range[0] range_end = range[1] if 'subnet' not in kwargs: raise ValueError('The "subnet" is a mandatory parameter!') subnet = kwargs.get('subnet') if 'server' not in kwargs: raise ValueError('The "server" is a mandatory parameter!') server = kwargs.get('server') interface = "eth1" netmask = '255.255.255.0' gateway_info = '' if 'interface' in kwargs: interface = kwargs.get('interface') if 'subnet' in kwargs: subnet = kwargs.get('subnet') if 'netmask' in kwargs: netmask = kwargs.get('netmaks') if 'gateway' in kwargs: gateway = kwargs.get('gateway') gateway_info = \ """ option routers {}; option subnet-mask {}; """.format(gateway, netmask) conf_info = \ """ ddns-update-style interim; ignore client-updates; subnet %s netmask %s { range dynamic-bootp %s %s; default-lease-time 300; max-lease-time 300; %s } """ % (subnet, netmask, range_start, range_end, gateway_info) service = "isc-dhcp-server" service_path = '/etc/default' service_file = '{}/{}'.format(service_path, service) self._access.shell("sed -i -e 's/INTERFACES=.*/INTERFACES=\"{}\"/g' {}".format(\ interface, service_file)) self.config_ip("{}/{}".format(server, netmask), interface) conf_path = '/etc/dhcp' conf_filename = "dhcpd.conf" conf_file = '{}/{}'.format(conf_path, conf_filename) self._access.shell("mv {} {}.bak".format(conf_file, conf_file)) self._access.shell("touch {}".format(conf_file)) for contents in va_parse_as_lines(conf_info): if len(contents) != 0: output = self._access.shell("echo '{}' >> {}".format( contents, conf_file)) try: self.service_stop(service) self.service_start(service) except ValueError as e: pass try: output = int(self._access.shell("netstat -uap |grep dhcpd -c")[0]) except: output = 0 if not output: logger.error('Failed to setup DHCP server') return (False) logger.info('Succeed to setup DHCP server') return True
def config_dns(self, *args, **kwargs): """ method to configure dns server param : kwargs : dict domain : domain name, record : a record of dns like "pc IN A 6.6.6.6" Returns: :bool - True on success or False on failure: example: config_dns(domain='varmour.net', record=['pc IN A 6.6.6.6']) """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) domain = 'varmour.net' record = 'pc IN A 6.6.6.6;' if 'domain' in kwargs: domain = kwargs.get('domain') if 'record' in kwargs: record = kwargs.get('record') if not isinstance(record, list): record = [record] soa_info = \ """ $TTL 86400 $ORIGIN {}. @ IN SOA pc.{}. master.pc.{}. ( 2008031101 ;Serial 86400 ;refresh 7200 ;retry 86400 ;expire 120 ;ttl ) """.format(domain, domain, domain) record_info = \ """ @ IN NS pc.{}. @ IN MX 5 pc test IN A 1.1.1.1; """.format(domain) for info in record: record_info = "{}\n{}".format(record_info, info) conf_info = \ """ zone "." { type hint; file "/etc/bind/db.root"; }; zone "localhost" { type master; file "/etc/bind/db.local"; }; zone "127.in-addr.arpa" { type master; file "/etc/bind/db.127"; }; zone "0.in-addr.arpa" { type master; file "/etc/bind/db.0"; }; zone "255.in-addr.arpa" { type master; file "/etc/bind/db.255"; }; zone "%s" IN { type master; file "/etc/bind/%s"; }; """ % (domain, domain) resolv_info = \ """ nameserver 127.0.0.1 """ service = "bind9" conf_path = '/etc/bind' conf_filename = "named.conf.default-zones" conf_file = '{}/{}'.format(conf_path, conf_filename) zone_info = soa_info + record_info zone_filename = domain zone_file = '{}/{}'.format(conf_path, zone_filename) resolv_file = '/etc/resolv.conf' for contents, f in zip([zone_info, conf_info, resolv_info], [zone_file, conf_file, resolv_file]): self._access.shell("mv {} {}.bak".format(f, f)) self._access.shell("touch {}".format(f)) for cont in va_parse_as_lines(contents): if len(cont) != 0: output = self._access.shell("echo '{}' >> {}".format( cont, f)) try: self.service_stop(service) self.service_start(service) except ValueError as e: logger.error(e) return False name, ip = self.send_dns_query(name='test.{}'.format(domain)) if ip != "1.1.1.1": logger.error('Failed to setup DNS server!') return False logger.info('Succeed to setup DNS server') return True
def va_get_chassis_database(self, *args, **kwargs): """ API to get 'show chassis database' param : kwargs : dict : dev_id : device id, 'all' by default return : a dict like this { 'Chassis db device info 1': { 'DEVICE': 'vArmour', 'DEVICE ID': '1', 'DEVICE TYPE': 'controller', 'LAST UP TIME': 'Tue Mar 7 19:40:07 2017', 'UUID': '564D6062-7AAA-D604-17FE-79163FFCE9B5', 'Licensed': 'No', 'UP COUNT': '1', 'CONF VERSION': '0xf', 'Chassis db slot info': '', 'MANAGEMENT GATEWAY': '10.11.120.1', 'CONF PUSH STATUS': 'done', 'ENABLED MGT SERVICE': 'https ssh', 'SLOT COUNT': '1', 'CONNECTION TYPE': 'LOCAL', 'Chassis db slot info 1': { 'SLOT ID': '0', 'Containing node info 1': { 'LAST UP TIME': 'Tue Mar 7 19:40:09 2017', 'TYPE': 'Control Node', 'CONF PUSH STATUS': 'done', 'NODE ID': '1', 'UP COUNT': '1', 'intf': [ intf(name='local', node='1/173', flag='00400100', address='84:49:15:ff:0:1', state='up', note=''), }, 'Containing node info 2': { 'NODE ID': '2', 'LAST UP TIME': 'Tue Mar 7 19:40:10 2017', 'TYPE': 'Routing Engine Node', 'UP COUNT': '1', 'CONF PUSH STATUS': 'done' }, 'Containing node info 3': { 'LAST UP TIME': 'Tue Mar 7 19:40:12 2017', 'TYPE': 'Service Node', 'SESSION THRESHOLD': '0', 'CONF PUSH STATUS': 'done', 'NODE ID': '3', 'EXCLUDE FROM RR': 'No', 'UP COUNT': '1' }, 'Containing node info 4': { 'LAST UP TIME': 'Tue Mar 7 19:40:14 2017', 'TYPE': 'Switch Node', 'CONF PUSH STATUS': 'done', 'NODE ID': '4', 'UP COUNT': '1', 'intf': [ intf(name='xe-1/0/3', node='4/007', flag='00000000', address='0:50:56:99:2b:e3', state='up', note=''), intf(name='xe-1/0/3.1', node='0x00403001', flag='00000000', address='0.0.0.0/00', state='up', note='') ] } } } } example : output = va_get_chassis_database(dev_id=1) output = va_get_chassis_database() for k, v in output.items(): get device info for x, y in v.items(): # get slot info try: for key, val in y.items(): # get node info if 'intf' in val: for intf_info in val.get('intf'): # get intf info from a list of intf intf_name = intf_info.name # get interface name, such as name='xe-1/0/3' intf_node = intf_info.node # get interface node, such as node='4/007' except AttributeError: pass """ logger.info("\nIn subroutine: " + sys._getframe().f_code.co_name) if 'dev_id' in kwargs: dev_id = kwargs.get('dev_id') else: dev_id = "all" cmd = "show chassis database device {}".format(dev_id) output = self._access.va_cli(cmd) #Split output with 'DEVICE ID' lines = va_parse_as_lines(output) info_index_list = list() for line in lines: line = line.strip() try: name, value = line.split(':') name = name.strip() except: name = None if name == 'DEVICE ID': info_index = lines.index(line) info_index_list.append((info_index - 1)) dev_id = 1 parsed = dict() for i in range(len(info_index_list)): try: s, e = info_index_list[i], info_index_list[i +1] except IndexError: s, e = info_index_list[i], -1 new_output = lines[s:e] parsed.update(self._va_parse_chassis_database(new_output, dev_id)) dev_id += 1 return(parsed)
def _va_parse_chassis_epi(self, output=None): """ helper method to parse 'show chassis epi uuid' or 'show chassis epi hostname' returns (dict): look at the following output { "Active EPI":[ Epi(uuid='420135BF-0069-120D-816E-29A058C829ED', hostname='VA-31-EPi1',ep_id='2-6',mode='tap','fabric_ip'='100.0.92.84/24', management_ip='10.150.92.84/16',connected_since='@Thu-Jul=28-09:13:56-2016', connected_type=TLS'',licensed='yes'), Epi(uuid='420135BF-0069-120D-816E-29A058C829ED', hostname='VA-31-EPi1',ep_id='2-7*',mode='tap','fabric_ip'='100.0.92.84/24', management_ip='10.150.92.84/16',connected_since='@Thu-Jul=28-09:13:56-2016', connected_type=TLS'',licensed='yes') ], "Inactive EPI":[Epi(uuid='564DDF39-5C53-66E4-4F64-6CB7B2B8BC9B',hostname='-',last_connected_ep='3',mode='tap')] "Total inactive EPIs":"1", "Total active EPIs":" 1" } """ print("==========================") print("4.04.0") parsed = dict() current_key = None active_tag = 0 inactive_tag = 0 parse_info = va_parse_as_lines(output) parse_info.pop(-1) for line in parse_info: line = line.lstrip() if line.startswith('Active'): active_tag = 1 inactive_tag = 0 current_key = line.rstrip(':') parsed[current_key] = list() elif line.startswith('Inactive'): active_tag = 0 inactive_tag = 1 current_key = line.rstrip(':') parsed[current_key] = list() elif not line.startswith('UUID')\ and not line.startswith('-')\ and not line.startswith('Total')\ and not line.startswith('*:'): if active_tag == 1: Epi = namedtuple('Epi', ['uuid', 'hostname', 'ep_id', 'mode', 'fabric_ip', 'management_ip', 'connected_since', 'connected_type', 'licensed', 'type']) else: Epi = namedtuple('Epi', ['uuid', 'hostname', 'last_connected_ep', 'mode']) values = line.split() parse_values = list() parse_tag = 0 parse_element = '' for list_element in values: if re.search('^@\w+$',list_element,re.I): parse_tag = 1 if parse_tag: if re.search('^\d{4}$',list_element): parse_tag = 0 parse_element += '%s' % list_element parse_values.append(parse_element) else: if re.search(r'INVALID$',list_element) : parse_element += '%s' % list_element[0:4] parse_values.append(parse_element) parse_values.append('INVALID') parse_tag = 0 else : parse_element += '%s-' % list_element else : if re.search('^\*$',list_element): parse_values[-1] += '*' else: parse_values.append(list_element) parsed[current_key].append(Epi(*parse_values)) elif line.startswith('Total'): key, value = line.split(':') parsed[key] = value else: continue return parsed