Esempio n. 1
0
 def test_firewall_address(self):
     cmd = cli_converter.convert_command('firewall_address.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"ffn-support-i1","subnet":"193.104.116.2 255.255.255.255"}'
     )
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'ffn-support-i1')
     self.assertEqual(cmd[0].path, 'firewall/address')
     self.assertEqual(
         cmd[1].body,
         '{"name":"net_sslvpn","associated-interface":"ssl.root",'
         '"subnet":"10.254.254.0 255.255.255.0"}')
     self.assertEqual(cmd[1].action, 'edit')
     self.assertEqual(cmd[1].name, 'net_sslvpn')
     self.assertEqual(cmd[1].path, 'firewall/address')
     self.assertEqual(
         cmd[2].body,
         '{"name":"grp_ffn-support","member":[{"name":"ffn-support-i1"},'
         '{"name":"ffn-support-i4"},{"name":"fqdn_gway.firstframe.net"},'
         '{"name":"fqdn_surfgate.firstframe.net"},'
         '{"name":"fqdn_manager.firstframe.net"}]}')
     self.assertEqual(cmd[2].action, 'edit')
     self.assertEqual(cmd[2].name, 'grp_ffn-support')
     self.assertEqual(cmd[2].path, 'firewall/addrgrp')
Esempio n. 2
0
 def test_proxy_options(self):
     cmd = cli_converter.convert_command('proxy_options.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"po_standard","http":{"ports":"80","options":"clientcomfort",'
         '"comfort-interval":"1","comfort-amount":"1024","post-lang":null},'
         '"ftp":{"ports":"21","options":"clientcomfort","comfort-interval":"1",'
         '"comfort-amount":"1024"},"imap":{"ports":"143","options":null},'
         '"mapi":{"ports":"135","options":null},"pop3":{"ports":"110","options":null},'
         '"smtp":{"ports":"25","options":null},"nntp":{"ports":"119","options":null},'
         '"im":{"options":null},"dns":{"ports":"53"}}')
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'po_standard')
     self.assertEqual(cmd[0].path, 'firewall/profile-protocol-options')
     self.assertEqual(
         cmd[1].body,
         '{"name":"po_standard","http":{"ports":"80","options":"clientcomfort",'
         '"comfort-interval":"1","comfort-amount":"1024","post-lang":null},'
         '"ftp":{"ports":"21","options":"clientcomfort","comfort-interval":"1",'
         '"comfort-amount":"1024"},"imap":{"ports":"143","options":null},'
         '"mapi":{"ports":"135","options":null},"pop3":{"ports":"110","options":null},'
         '"smtp":{"ports":"25","options":null},"nntp":{"ports":"119","options":null},'
         '"im":{"options":null},"dns":{"ports":"53"}}')
     self.assertEqual(cmd[1].action, 'edit')
     self.assertEqual(cmd[1].name, 'po_standard')
     self.assertEqual(cmd[1].path, 'firewall/profile-protocol-options')
Esempio n. 3
0
 def test_delete_command(self):
     cmd = cli_converter.convert_command('delete.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(cmd[0].body, '')
     self.assertEqual(cmd[0].action, 'delete')
     self.assertEqual(cmd[0].name, '1')
     self.assertEqual(cmd[0].path, 'firewall/policy')
Esempio n. 4
0
 def test_local_in_policy(self):
     cmd = cli_converter.convert_command('local_in_policy.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body, '{"intf":"internet","srcaddr":[{"name":"all"}],'
         '"dstaddr":[{"name":"all"}],"action":"accept","service":[{"name":"PING"},'
         '{"name":"IKE"},{"name":"ESP"},{"name":"HTTPS"},{"name":"fortisslvpn"}],'
         '"schedule":"always"}')
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, '0')
     self.assertEqual(cmd[0].path, 'firewall/local-in-policy')
     self.assertEqual(
         cmd[1].body,
         '{"intf":"internet","srcaddr":[{"name":"grp_ffn-support"},'
         '{"name":"grp_forti-support"}],"dstaddr":[{"name":"all"}],"action":"accept",'
         '"service":[{"name":"SSH"},{"name":"fortiadmin"},{"name":"ALL_ICMP"}],'
         '"schedule":"always"}')
     self.assertEqual(cmd[1].action, 'edit')
     self.assertEqual(cmd[1].name, '0')
     self.assertEqual(cmd[1].path, 'firewall/local-in-policy')
     self.assertEqual(
         cmd[2].body, '{"intf":"internet","srcaddr":[{"name":"all"}],'
         '"dstaddr":[{"name":"all"}],"service":[{"name":"ALL"}],"schedule":"always"}'
     )
     self.assertEqual(cmd[2].action, 'edit')
     self.assertEqual(cmd[2].name, '0')
     self.assertEqual(cmd[2].path, 'firewall/local-in-policy')
Esempio n. 5
0
 def test_ssl_vpn(self):
     cmd = cli_converter.convert_command('ssl_vpn.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(cmd[0].body, '')
     self.assertEqual(cmd[0].action, 'delete')
     self.assertEqual(cmd[0].name, 'full-access')
     self.assertEqual(cmd[0].path, 'vpn.ssl.web/portal')
     self.assertEqual(cmd[1].body, '')
     self.assertEqual(cmd[1].action, 'delete')
     self.assertEqual(cmd[1].name, 'tunnel-access')
     self.assertEqual(cmd[1].path, 'vpn.ssl.web/portal')
     self.assertEqual(cmd[2].body, '')
     self.assertEqual(cmd[2].action, 'delete')
     self.assertEqual(cmd[2].name, 'web-access')
     self.assertEqual(cmd[2].path, 'vpn.ssl.web/portal')
     self.assertEqual(
         cmd[3].body,
         '{"name":"none","web-mode":"enable","limit-user-logins":"enable",'
         '"user-bookmark":"disable","display-connection-tools":"disable",'
         '"display-history":"disable","display-status":"disable",'
         '"heading":"SSL-VPN Dummypage"}')
     self.assertEqual(cmd[3].action, 'edit')
     self.assertEqual(cmd[3].name, 'none')
     self.assertEqual(cmd[3].path, 'vpn.ssl.web/portal')
     self.assertEqual(
         cmd[4].body,
         '{"sslv3":"disable","servercert":"Fortinet_Factory","algorithm":"high",'
         '"idle-timeout":"3600","auth-timeout":"43200",'
         '"tunnel-ip-pools":[{"name":"net_sslvpn"}],"port":"443",'
         '"source-interface":[{"name":"internet"}],"source-address":[{"name":"all"}],'
         '"source-address6":[{"name":"all"}],"default-portal":"none"}')
     self.assertEqual(cmd[4].action, '')
     self.assertEqual(cmd[4].name, '')
     self.assertEqual(cmd[4].path, 'vpn.ssl/settings')
Esempio n. 6
0
 def test_device_settings(self):
     cmd = cli_converter.convert_command('device_settings.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(cmd[0].body,
                      '{"admin-sport":"8443","admintimeout":"240"}')
     self.assertEqual(cmd[0].action, '')
     self.assertEqual(cmd[0].name, '')
     self.assertEqual(cmd[0].path, 'system/global')
Esempio n. 7
0
 def test_interface_zone(self):
     cmd = cli_converter.convert_command('interface_zone.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"internet","interface":[{"interface-name":"wan1"},{"interface-name":"wan2"}]}'
     )
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'internet')
     self.assertEqual(cmd[0].path, 'system/zone')
Esempio n. 8
0
 def test_antivirus_profile(self):
     cmd = cli_converter.convert_command('antivirus_profile.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"av_standard","inspection-mode":"proxy","http":{"options":"scan"},'
         '"ftp":{"options":"scan"},"imap":{"options":"scan"},"pop3":{"options":"scan"},'
         '"smtp":{"options":"scan"},"mapi":{"options":"scan"}}')
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'av_standard')
     self.assertEqual(cmd[0].path, 'antivirus/profile')
Esempio n. 9
0
 def test_rename(self):
     cmd = cli_converter.convert_command('rename.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(cmd[0].body, '{"desc":"allow"}')
     self.assertEqual(cmd[0].action, 'rename')
     self.assertEqual(cmd[0].name, 'allow')
     self.assertEqual(cmd[0].path, 'webfilter/ftgd-local-cat/custom1')
     self.assertEqual(cmd[1].body, '{"desc":"block"}')
     self.assertEqual(cmd[1].action, 'rename')
     self.assertEqual(cmd[1].name, 'block')
     self.assertEqual(cmd[1].path, 'webfilter/ftgd-local-cat/custom2')
Esempio n. 10
0
 def test_firewall_policy(self):
     cmd = cli_converter.convert_command('firewall_policy.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"srcintf":[{"name":"internal"}],"dstintf":[{"name":"internet"}],'
         '"srcaddr":[{"name":"all"}],"dstaddr":[{"name":"all"}],"action":"accept","schedule":"always",'
         '"service":[{"name":"ALL"}],"logtraffic":"all","comments":"fge test","nat":"enable"}'
     )
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, '0')
     self.assertEqual(cmd[0].path, 'firewall/policy')
Esempio n. 11
0
 def test_app_conrol(self):
     cmd = cli_converter.convert_command('app_control.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"ac_standard","other-application-log":"enable","unknown-application-log":"enable",'
         '"entries":[{"category":[{"id":"6"},{"id":"19"}]},{"category":[{"id":"2"},{"id":"3"},'
         '{"id":"5"},{"id":"7"},{"id":"8"},{"id":"12"},{"id":"15"},{"id":"17"},{"id":"21"},'
         '{"id":"22"},{"id":"23"},{"id":"25"},{"id":"26"},{"id":"28"},{"id":"29"},{"id":"30"},'
         '{"id":"31"}],"action":"pass"}]}')
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'ac_standard')
     self.assertEqual(cmd[0].path, 'application/list')
Esempio n. 12
0
 def test_policy_settings(self):
     cmd = cli_converter.convert_command('policy_settings.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"dpo","accprofile":"super_admin","vdom":"root","password":'******'"ENC AK16D3CKqjt+2aM/xf2ieZJcDoFdx2MhS5TxQWngTJi61s="}')
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'dpo')
     self.assertEqual(cmd[0].path, 'system/admin')
     self.assertEqual(
         cmd[1].body,
         '{"name":"dgu","accprofile":"super_admin","vdom":"root","password":'******'"ENC AK1P+a8jSjvASnxe4qTPCFGtCkpZYhdUJDWC1IzLHQVRe0="}')
     self.assertEqual(cmd[1].action, 'edit')
     self.assertEqual(cmd[1].name, 'dgu')
     self.assertEqual(cmd[1].path, 'system/admin')
Esempio n. 13
0
 def test_ips_sensor(self):
     cmd = cli_converter.convert_command('ips_sensor.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"ips_outgoing","entries":[{"location":"client ","severity":"critical ",'
         '"os":"Other Windows Linux MacOS ","status":"enable","log-packet":"enable","action":"block"},'
         '{"location":"client ","severity":"high ","os":"Other Windows Linux MacOS "}]}'
     )
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'ips_outgoing')
     self.assertEqual(cmd[0].path, 'ips/sensor')
     self.assertEqual(
         cmd[1].body,
         '{"name":"ips_incoming","entries":[{"location":"server ","severity":"critical ",'
         '"os":"Other Windows Linux ","status":"enable","log-packet":"enable","action":"block"},'
         '{"location":"server ","severity":"high ","os":"Other Windows Linux "}]}'
     )
     self.assertEqual(cmd[1].action, 'edit')
     self.assertEqual(cmd[1].name, 'ips_incoming')
     self.assertEqual(cmd[1].path, 'ips/sensor')
Esempio n. 14
0
 def test_firewall_service(self):
     cmd = cli_converter.convert_command('firewall_service.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"fortimanager","category":"Remote Access","tcp-portrange":"541"}'
     )
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'fortimanager')
     self.assertEqual(cmd[0].path, 'firewall.service/custom')
     self.assertEqual(cmd[1].body,
                      '{"name":"fortiadmin","tcp-portrange":"8443"}')
     self.assertEqual(cmd[1].action, 'edit')
     self.assertEqual(cmd[1].name, 'fortiadmin')
     self.assertEqual(cmd[1].path, 'firewall.service/custom')
     self.assertEqual(
         cmd[2].body,
         '{"name":"guest-services","member":[{"name":"HTTP"},{"name":"HTTPS"},'
         '{"name":"IMAP"},{"name":"IMAPS"},{"name":"POP3"},{"name":"POP3S"},'
         '{"name":"SMTP"},{"name":"SMTPS"},{"name":"FTP"},{"name":"PING"},'
         '{"name":"DNS"}]}')
     self.assertEqual(cmd[2].action, 'edit')
     self.assertEqual(cmd[2].name, 'guest-services')
     self.assertEqual(cmd[2].path, 'firewall.service/group')
Esempio n. 15
0
 def test_webfilter_profile(self):
     cmd = cli_converter.convert_command('webfilter_profile.txt',
                                         'C:/BulkChanger/input')
     self.assertEqual(
         cmd[0].body,
         '{"name":"wf_standard","override":{"ovrd-user-group":""},'
         '"web":{"log-search":"enable"},'
         '"ftgd-wf":{"options":"http-err-detail redir-block",'
         '"category-override":"140 141","filters":[{"id":"1","category":"26",'
         '"action":"block"},{"id":"2","category":"61","action":"block"},'
         '{"id":"3","category":"86","action":"block"},{"id":"4","category":"88",'
         '"action":"block"},{"id":"7","action":"block"},{"id":"6","category":"89"},'
         '{"id":"8","category":"140"},{"id":"9","category":"141","action":"block"}]},'
         '"comment":null,"log-all-url":null,"web-content-log":null,'
         '"web-filter-activex-log":null,"web-filter-command-block-log":null,'
         '"web-filter-cookie-log":null,"web-filter-applet-log":null,'
         '"web-filter-jscript-log":null,"web-filter-js-log":null,'
         '"web-filter-vbs-log":null,"web-filter-unknown-log":null,'
         '"web-filter-referer-log":null,"web-filter-cookie-removal-log":null,'
         '"web-url-log":null,"web-invalid-domain-log":null,"web-ftgd-err-log":null,'
         '"web-ftgd-quota-usage":null}')
     self.assertEqual(cmd[0].action, 'edit')
     self.assertEqual(cmd[0].name, 'wf_standard')
     self.assertEqual(cmd[0].path, 'webfilter/profile')
Esempio n. 16
0
    def start_bulk(self):
        # LOG FILE
        if getattr(sys, 'frozen', False):
            log_path = os.path.join(
                os.path.abspath(os.path.dirname(sys.executable)),
                'bulkchanger.log')
        else:
            log_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                    'bulkchanger.log')
        if not os.path.isfile(log_path):
            temp_file = open(log_path, 'w+')
            temp_file.close()
        logging.basicConfig(filemode='w',
                            filename=log_path,
                            level=logging.INFO,
                            format='%(asctime)s %(levelname)s %(message)s',
                            datefmt='%Y-%m-%d %H:%M:%S')
        if Config().log_level.upper() == 'DEBUG':
            logging.getLogger().setLevel(logging.DEBUG)

        # CREDENTIALS
        if not ((Config().firewall_user == '') or
                (Config().firewall_password == '')):
            self.firewall_user = Config().firewall_user
            self.firewall_password = Config().firewall_password

        if not ((Config().sslvpn_user == '') or
                (Config().sslvpn_password == '')):
            self.sslvpn_user = Config().sslvpn_user
            self.sslvpn_password = Config().sslvpn_password

        # GET COMMANDS
        if getattr(sys, 'frozen', False):
            input_file_path = os.path.abspath(os.path.dirname(sys.executable))
        else:
            input_file_path = os.path.join(
                os.path.abspath(os.path.dirname(__file__)), 'input')
        cmd = cli_converter.convert_command('input.txt', input_file_path)
        if not cmd:
            self.exception_callback('command line input')
            self.stop()
        cli_converter.marshalling(cmd)

        # START EXECUTION
        self.device.session = Session()
        bodyhash = {
            'username': self.firewall_user,
            'secretkey': self.firewall_password
        }
        try:
            self.device.session.post('https://' + self.device.ip + ':' +
                                     self.device.port + '/logincheck',
                                     data=bodyhash,
                                     verify=False,
                                     timeout=self.device.timeout)
        except RequestException:
            self.append_callback('red', self.device.customer)
            print('execption during login')
            return
        for cookie in self.device.session.cookies:
            try:
                if cookie.name == 'ccsrftoken':
                    csrftoken = cookie.value[1:-1]
                    self.device.session.headers.update(
                        {'X-CSRFTOKEN': csrftoken})
            except:
                self.append_callback('red', self.device.customer)
                print('error with cookie')
                return
        executor.perform_backup(self.device, 'before')
        executor.run_command(self.device, cmd)
        executor.perform_backup(self.device, 'after')
        self.device.session.post('https://' + self.device.ip + ':' +
                                 self.device.port + '/logout')
        self.append_callback('green', self.device.customer)
        program = 'C:/Program Files (x86)/Notepad++/plugins/ComparePlugin/compare.exe'
        file1 = Config().backup_folder + '/' + 'before.conf'
        file2 = Config().backup_folder + '/' + 'after.conf'
        subprocess.Popen([program, file1, file2], shell=True)
Esempio n. 17
0
    def start_bulk(self):
        # VARIABLES
        # devices = []
        failed_devices = []
        skipped_devices = []
        success_devices = []
        duplicates = 0
        # cmd = []
        # sslvpn_user = None
        # sslvpn_password = None
        sslconnected = False

        # LOG FILE
        # REMOVE LOGGING HANDLERS
        for handler in logging.root.handlers[:]:
            logging.root.removeHandler(handler)

        if getattr(sys, 'frozen', False):
            log_path = os.path.join(
                os.path.abspath(os.path.dirname(sys.executable)),
                'bulkchanger.log')
        else:
            log_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                    'bulkchanger.log')
        if not os.path.isfile(log_path):
            temp_file = open(log_path, 'w+')
            temp_file.close()
        logging.basicConfig(filemode='w',
                            filename=log_path,
                            level=logging.INFO,
                            format='%(asctime)s %(levelname)s %(message)s',
                            datefmt='%Y-%m-%d %H:%M:%S')
        if Config().log_level.upper() == 'DEBUG':
            logging.getLogger().setLevel(logging.DEBUG)

        # CREDENTIALS
        if not ((Config().firewall_user == '') or
                (Config().firewall_password == '')):
            self.firewall_user = Config().firewall_user
            self.firewall_password = Config().firewall_password

        if not ((Config().sslvpn_user == '') or
                (Config().sslvpn_password == '')):
            self.sslvpn_user = Config().sslvpn_user
            self.sslvpn_password = Config().sslvpn_password

        # GET FIREWALL LIST
        devices = customers.collect_firewalls()
        if not devices:
            logging.error('devices: no firewalls found, abort execution')
            self.exception_callback('devices')
            self.stop()

        # COLLECT SSL VPN PROFILES:
        sslprofile = sslvpn.collect()
        if not sslprofile:
            logging.error('sslvpn: no profiles found, abort execution')
            self.exception_callback('ssl vpn profile')
            self.stop()

        # GET COMMANDS
        if getattr(sys, 'frozen', False):
            input_file_path = os.path.abspath(os.path.dirname(sys.executable))
        else:
            input_file_path = os.path.join(
                os.path.abspath(os.path.dirname(__file__)), 'input')
        cmd = cli_converter.convert_command('input.txt', input_file_path)
        if not cmd:
            self.exception_callback('command line input')
            self.stop()
        cli_converter.marshalling(cmd)
        # print_cmd(cmd)
        # exit()

        # START EXECUTION
        for device in devices:
            if not self.runs:
                break
            self.status_callback(len(devices), len(success_devices),
                                 len(failed_devices), len(skipped_devices),
                                 duplicates)
            logging.info(
                '******************************************************************'
            )
            logging.info('IP: ' + device.ip + '\t Port:' + device.port +
                         '\t Customer: ' + device.customer)
            if device.check_ip():
                # check if local device
                if device.ping():
                    index = sslvpn.match(device.customer, sslprofile)
                    if index == -1:
                        logging.warning(
                            'sslvpn: found no matching ssl profile')
                        failed_devices.append(device)
                        device.reason = 'private ip and no sslvpn profile'
                        self.append_callback('red', device.customer)
                        continue
                    if not sslvpn.connect(
                            sslprofile[index].ip, sslprofile[index].port,
                            self.sslvpn_user, self.sslvpn_password):
                        self.exception_callback('sslvpn connect')
                        self.stop()
            if device.ping():
                # try to login anyway
                device.login(self.firewall_user, self.firewall_password)
                if not device.connected:
                    logging.warning('ping: device is offline, skip device')
                    failed_devices.append(device)
                    device.reason = 'no ping response'
                    self.append_callback('red', device.customer)
                    continue
            logging.debug('ping: device is online')
            device.login(self.firewall_user, self.firewall_password)
            if not device.connected:
                device.reason = 'wrong username or password'
            elif device.connected:
                device.firmware_check()
            if not device.connected:
                failed_devices.append(device)
                self.append_callback('red', device.customer)
                continue
            if device.check_duplicate(devices, devices.index(device)):
                duplicates += 1
                self.append_callback('blue', device.customer)
                continue
            executor.fmg_check(device)
            if device.fortimanager == 'enable':
                skipped_devices.append(device)
                self.append_callback('purple', device.customer)
                continue
            executor.vdom_check(device)
            if device.vdom_mode == 'enable':
                skipped_devices.append(device)
                self.append_callback('purple', device.customer)
                continue
            if Config().backup_enable.upper(
            ) == 'TRUE' and not '5.2' in device.firmware:
                executor.perform_backup(device)
            executor.run_command(device, cmd)
            device.logout()
            logging.debug('ping: device is still online')
            success_devices.append(device)
            self.append_callback('green', device.customer)

        self.status_callback(len(devices), len(success_devices),
                             len(failed_devices), len(skipped_devices),
                             duplicates)

        if sslconnected:
            sslconnected = False
            sslvpn.disconnect()

        # DELTE TEMPORARY FILES
        file = input_file_path + '/ca.cer'
        try:
            os.remove(file)
        except FileNotFoundError:
            logging.debug('certifiacte: source file not exist')

        # PRINT RESULT
        executor.run_summary(log_path, len(devices), len(success_devices),
                             failed_devices, duplicates, skipped_devices)

        # REMOVE LOGGING HANDLERS
        for handler in logging.root.handlers[:]:
            logging.root.removeHandler(handler)

        self.end_callback()