Exemplo n.º 1
0
    def replace(self, qiss, qtag, info):
        """
        read entity configuration and replace if changed
        
        :param qiss: OP issuer qoute_plus converted
        :param qtag: test instance tag quote_plus converted
        :param info: test instance configuration as JSON document

        """
        uqp, qp = unquote_quote(qiss, qtag)
        logger.info('Replace config: iss="{}", tag="{}"'.format(*uqp))
        try:
            _js = json.loads(info)
        except Exception as err:
            _desc = 'Bogus replacement info!: {}'.format(info)
            logger.error(_desc)
            return cherrypy.HTTPError(404, _desc)

        try:
            _js0 = self.read_conf(qiss, qtag)
        except (TypeError, NoSuchFile):
            path = '{}/{}'.format(qiss, qtag)
            return cherrypy.HTTPError(404, 'Could not find {}'.format(path))

        if _js == _js0:  # don't bother
            pass
        else:
            self.write(qiss, qtag, json.dumps(_js))

        return b'OK'
Exemplo n.º 2
0
    def run(self, iss='', tag='', ev=None, **kwargs):
        uqp, qp = unquote_quote(iss, tag)
        logger.info('Run iss="{}", tag="{}"'.format(*uqp))

        self.store_edit(qp, **kwargs)

        return self.restart_instance(iss, tag, 'start')
Exemplo n.º 3
0
    def construct_config(self, qiss, qtag):
        uqp, qp = unquote_quote(qiss, qtag)

        logger.info('construct config iss="{}", tag="{}"'.format(*uqp))
        if not qtag:
            raise Exception('Missing "tag" value')

        _conf = json.loads(
            open('{}/common.json'.format(self.entinfo), 'r').read())

        typ, _econf = self.read_conf(*qp)

        if _econf is None:
            raise Exception('No configuration for {}:{}'.format(*uqp))

        if do_registration(_econf['tool']['profile']):
            reg_info = json.loads(
                open('{}/registration_info.json'.format(self.entinfo),
                     'r').read())
            _conf['client']['registration_info'] = reg_info[
                'registration_info']
        else:
            try:
                _conf['client']['registration_response'] = _econf['client'][
                    'registration_response']
            except KeyError:
                _conf['client']['registration_response'] = _econf[
                    'registration_response']

        if not do_discovery(_econf['tool']['profile']):
            _conf['client']['provider_info'] = _econf['provider_info']

        _conf['tool'] = _econf['tool']
        logger.info("Constructed config: {}".format(_conf))
        return _conf
Exemplo n.º 4
0
    def list_dir(self, dirname, qiss):
        uqp, qp = unquote_quote(qiss)
        logger.info('List directory: iss="{}"'.format(uqp[0]))

        if not os.path.isdir(dirname):
            if qp[0].endswith('%2F'):  # try to remove
                qp[0] = qp[0][:-3]
            else:  # else add
                qp[0] += '%2F'
            dirname = self.entity_file_name(qp[0], '')
            if not os.path.isdir(dirname):
                raise ValueError(dirname)

        res = ['<p>']
        for file in os.listdir(dirname):
            _url = '{}{}/{}'.format(self.base_url, qp[0], quote_plus(file))
            res.append('<a href="{}">{}</a><br>'.format(_url, file))
        res.append('</p')
        _html = [
            '<html><head>List of tags for "{}"</head>'.format(uqp[0]), '<body>'
        ]
        _html.extend(res)
        _html.append('</body></html>')

        return 'html', '\n'.join(_html)
Exemplo n.º 5
0
    def run(self, iss='', tag='', ev=None, **kwargs):
        uqp, qp = unquote_quote(iss, tag)
        logger.info('Run iss="{}", tag="{}"'.format(*uqp))

        self.store_edit(qp, **kwargs)

        return self.restart_instance(iss, tag, 'start')
Exemplo n.º 6
0
    def restart_instance(self, iss, tag, action='restart'):
        uqp, qp = unquote_quote(iss, tag)
        logger.info('{} iss="{}", tag="{}"'.format(action, uqp[0], uqp[1]))
        url = self.app.run_test_instance(qp[0], qp[1])

        if isinstance(url, Response):
            return conv_response(None, url)

        if url:
            args = {
                'title':
                "Action performed",
                'base':
                self.baseurl,
                'note':
                'Your test instance "{iss}:{tag}" has been '
                '{act} as <a href="{url}">{url}</a>'.format(iss=uqp[0],
                                                            tag=uqp[1],
                                                            url=url,
                                                            act=action)
            }
        else:
            args = {
                'title': "Action Failed",
                'base': self.baseurl,
                'note': 'Could not {} your test instance'.format(action)
            }

        _msg = self.html['message.html'].format(**args)
        return as_bytes(_msg)
Exemplo n.º 7
0
    def list_tag(self, iiss):
        uqp, qp = unquote_quote(iiss)
        logger.info('List all tags for "{}"'.format(uqp[0]))
        iss = uqp[0]
        qiss = qp[0]
        try:
            fils = os.listdir(os.path.join(self.entpath, qiss))
        except FileNotFoundError:
            logger.warning('No such Issuer exists')
            return b"No such Issuer exists"

        active = dict()
        tags = []
        for fil in fils:
            tag = unquote_plus(fil)
            active[tag] = isrunning(iss, tag)
            tags.append(tag)

        logger.info('tags: {}'.format(tags))

        self.assigned_ports.load()
        _msg = self.prehtml['list_tag.html'].format(item_table=item_table(
            qiss, tags, active, self.assigned_ports, self.test_tool_base),
                                                    iss=iss,
                                                    version=self.version)
        return as_bytes(_msg)
Exemplo n.º 8
0
    def update(self, iss, tag, ev=None, **kwargs):
        """
        Displays interface for updating configuration

        :param iss: Issuer ID 
        :param tag: tag
        :param ev: Event instance
        :param kwargs: keyword arguments
        :return: 
        """
        logger.debug('update test tool configuration: {} {}'.format(iss, tag))
        uqp, qp = unquote_quote(iss, tag)

        try:
            _format, _conf = self.rest.read_conf(qp[0], qp[1])
        except TypeError:
            _msg = "No such test tool configuration"
            logger.info(_msg)
        else:
            logger.info('config: {}'.format(_conf))

            dicts, state, multi, notes = update_config(_conf, self.tool_params)

            action = "{}/run/{}/{}".format('', qp[0], qp[1])
            _msg = self.html['instance.html'].format(display=display(
                dicts, state, multi, notes, action),
                                                     version=self.version)

        return as_bytes(_msg)
Exemplo n.º 9
0
    def restart(self, iss, tag, ev):
        """
        Restart a test instance

        :param iss: 
        :param tag: 
        :param ev: 
        :return: 
        """
        logger.info('restart test tool: {} {}'.format(iss, tag))
        uqp, qp = unquote_quote(iss, tag)
        url = self.app.run_test_instance(*qp)

        if isinstance(url, Response):
            return conv_response(None, url)

        if url:
            # redirect back to entity page
            loc = '{}entity/{}'.format(self.rest.base_url, qp[0])
            raise cherrypy.HTTPRedirect(loc)
        else:
            args = {
                'title': "Action Failed", 'base': self.baseurl,
                'note': 'Could not restart your test instance'}

        _msg = self.html['message.html'].format(**args)
        return as_bytes(_msg)
Exemplo n.º 10
0
    def update(self, iss, tag, ev=None, **kwargs):
        """
        Displays interface for updating configuration

        :param iss: Issuer ID 
        :param tag: tag
        :param ev: Event instance
        :param kwargs: keyword arguments
        :return: 
        """
        logger.debug('update test tool configuration: {} {}'.format(iss, tag))
        uqp, qp = unquote_quote(iss, tag)

        try:
            _format, _conf = self.rest.read_conf(qp[0], qp[1])
        except TypeError:
            _msg = "No such test tool configuration"
            logger.info(_msg)
        else:
            logger.info('config: {}'.format(_conf))

            dicts, state, multi, notes = update_config(_conf, self.tool_params)

            action = "{}/run/{}/{}".format('', qp[0], qp[1])
            _msg = self.html['instance.html'].format(
                display=display(dicts, state, multi, notes, action),
                version=self.version
            )

        return as_bytes(_msg)
Exemplo n.º 11
0
    def list_tag(self, iiss):
        uqp, qp = unquote_quote(iiss)
        logger.info('List all tags for "{}"'.format(uqp[0]))
        iss = uqp[0]
        qiss = qp[0]
        try:
            fils = os.listdir(os.path.join(self.entpath, qiss))
        except FileNotFoundError:
            logger.warning('No such Issuer exists')
            return b"No such Issuer exists"

        active = dict()
        tags = []
        for fil in fils:
            tag = unquote_plus(fil)
            active[tag] = isrunning(iss, tag)
            tags.append(tag)

        logger.info('tags: {}'.format(tags))

        self.assigned_ports.load()
        _msg = self.prehtml['list_tag.html'].format(
            item_table=item_table(qiss, tags, active, self.assigned_ports, self.test_tool_base),
            iss=iss,
            version=self.version
        )
        return as_bytes(_msg)
Exemplo n.º 12
0
    def restart(self, iss, tag, ev):
        """
        Restart a test instance

        :param iss: 
        :param tag: 
        :param ev: 
        :return: 
        """
        logger.info('restart test tool: {} {}'.format(iss, tag))
        uqp, qp = unquote_quote(iss, tag)
        url = self.app.run_test_instance(*qp)

        if isinstance(url, Response):
            return conv_response(None, url)

        if url:
            # redirect back to entity page
            loc = '{}entity/{}'.format(self.rest.base_url, qp[0])
            raise cherrypy.HTTPRedirect(loc)
        else:
            args = {
                'title': "Action Failed",
                'base': self.baseurl,
                'note': 'Could not restart your test instance'
            }

        _msg = self.html['message.html'].format(**args)
        return as_bytes(_msg)
Exemplo n.º 13
0
    def create(self, **kwargs):
        logging.info('create test tool configuration')
        # construct profile
        profile = to_profile(kwargs)
        _ent_conf = create_model(profile, ent_info_path=self.ent_info_path)
        state = {}

        if not do_discovery(profile):
            _ent_conf['client']['provider_info']['issuer'] = kwargs['iss']

        if not do_registration(profile):
            # need to create a redirect_uri, means I need to register a port
            _port = self.app.assigned_ports.register_port(
                kwargs['iss'], kwargs['tag'])
            if self.app.test_tool_base.endswith('/'):
                _base = self.app.test_tool_base[:-1]
            else:
                _base = self.app.test_tool_base
            _ent_conf['client']['registration_response'][
                'redirect_uris'] = '{}:{}/authz_cb'.format(_base, _port)

        uqp, qp = unquote_quote(kwargs['iss'], kwargs['tag'])
        _ent_conf['tool']['issuer'] = uqp[0]
        _ent_conf['tool']['tag'] = uqp[1]
        _ent_conf['tool']['profile'] = profile

        _ent_conf.update(from_profile(profile))
        logging.info("Test tool config: {}".format(_ent_conf))

        self.rest.write(qp[0], qp[1], _ent_conf)
        # Do a redirect
        raise cherrypy.HTTPRedirect('/action/update?iss={}&tag={}'.format(
            qp[0], qp[1]))
Exemplo n.º 14
0
    def store(self, qiss, qtag, info):
        """

        :param qiss: OP issuer qoute_plus converted
        :param qtag: test instance tag quote_plus converted
        :param info: test instance configuration as JSON document
        :return: HTTP Created is successful
        """
        uqp, qp = unquote_quote(qiss, qtag)
        logger.info('Store config: iss="{}", tag="{}", info={}'.format(
            uqp[0], uqp[1], info))
        # verify the soundness of the information
        if isinstance(info, dict):
            info = json.dumps(info)
        else:
            try:
                json.loads(info)
            except Exception as err:
                _desc = 'Bogus replacement info!: {}'.format(info)
                logger.error(_desc)
                return cherrypy.HTTPError(404, _desc)

        self.write(qiss, qtag, info)
        fname = '{}{}/{}'.format(self.base_url, qiss, qtag)
        cherrypy.response.status = 201
        return as_bytes(fname)
Exemplo n.º 15
0
    def backup(self, iiss, itag):
        uqp, qp = unquote_quote(iiss, itag)
        logger.info('Do backup of iss="{}", tag="{}"'.format(*uqp))

        info = open(os.path.join(self.entpath, *qp), 'r').read()
        bname = '{}.{}.{}'.format(qp[0], qp[1], time.time())
        fp = open(os.path.join(self.backup, bname), 'w')
        fp.write(info)
        fp.close()
        return ''
Exemplo n.º 16
0
    def backup(self, iiss, itag):
        uqp, qp = unquote_quote(iiss, itag)
        logger.info('Do backup of iss="{}", tag="{}"'.format(*uqp))

        info = open(os.path.join(self.entpath, *qp), 'r').read()
        bname = '{}.{}.{}'.format(qp[0], qp[1], time.time())
        fp = open(os.path.join(self.backup, bname), 'w')
        fp.write(info)
        fp.close()
        return ''
Exemplo n.º 17
0
    def stop(self, iss, tag, ev):
        logger.info('stop test tool: {} {}'.format(iss, tag))

        # If already running - kill
        self.kill(iss, tag, ev)

        uqp, qp = unquote_quote(iss, tag)

        # redirect back to entity page
        loc = '{}entity/{}'.format(self.rest.base_url, qp[0])
        raise cherrypy.HTTPRedirect(loc)
Exemplo n.º 18
0
    def stop(self, iss, tag, ev):
        logger.info('stop test tool: {} {}'.format(iss, tag))

        # If already running - kill
        self.kill(iss, tag, ev)

        uqp, qp = unquote_quote(iss, tag)

        # redirect back to entity page
        loc = '{}entity/{}'.format(self.rest.base_url, qp[0])
        raise cherrypy.HTTPRedirect(loc)
Exemplo n.º 19
0
    def show_tag(self, iiss, itag):
        uqp, qp = unquote_quote(iiss, itag)
        logger.info('Show info on iss="{}", tag="{}"'.format(*uqp))

        if find_test_instance(*uqp):
            active = '<div class="active"> Running </div>'
        else:
            active = '<div class="inactive"> Inactive </div>'

        info = open(os.path.join(self.entpath, *qp), 'r').read()

        _msg = self.prehtml['action.html'].format(path=qp[-1], active=active,
                                                  display_info=info)
        return as_bytes(_msg)
Exemplo n.º 20
0
    def show_tag(self, iiss, itag):
        uqp, qp = unquote_quote(iiss, itag)
        logger.info('Show info on iss="{}", tag="{}"'.format(*uqp))

        if find_test_instance(*uqp):
            active = '<div class="active"> Running </div>'
        else:
            active = '<div class="inactive"> Inactive </div>'

        info = open(os.path.join(self.entpath, *qp), 'r').read()

        _msg = self.prehtml['action.html'].format(path=qp[-1], active=active,
                                                  display_info=info)
        return as_bytes(_msg)
Exemplo n.º 21
0
    def create(self, **kwargs):
        logger.info('create test tool configuration: {} {}'.format(
            kwargs['iss'], kwargs['tag']))

        uqp, qp = unquote_quote(kwargs['iss'], kwargs['tag'])
        if not uqp[0].startswith('https://') and not uqp[0].startswith(
                'http://'):
            err = 'issuer value must start with "https://" or "http://"'
            logger.error(err)
            return as_bytes('Sorry failed to create: {}'.format(err))

        # construct profile
        try:
            profile = to_profile(kwargs)
        except KeyError as err:
            logger.error(err)
            return as_bytes('Sorry failed to create: {}'.format(err))

        _ent_conf = create_model(profile, ent_info_path=self.ent_info_path)

        if not do_discovery(profile):
            _ent_conf['client']['provider_info']['issuer'] = kwargs['iss']

        if not do_registration(profile):
            # need to create a redirect_uri, means I need to register a port
            _port = self.app.assigned_ports.register_port(
                kwargs['iss'], kwargs['tag'])
            if self.app.test_tool_base.endswith('/'):
                _base = self.app.test_tool_base[:-1]
            else:
                _base = self.app.test_tool_base
            _ent_conf['client']['registration_response'][
                'redirect_uris'] = '[ "{}:{}/authz_cb", "{}:{}/authz_post" ]'.format(
                    _base, _port, _base, _port)

        _ent_conf['tool']['issuer'] = uqp[0]
        _ent_conf['tool']['tag'] = uqp[1]
        _ent_conf['tool']['profile'] = profile

        _ent_conf.update(from_profile(profile))
        logger.info("Test tool config: {}".format(_ent_conf))

        self.rest.write(qp[0], qp[1], _ent_conf)
        # Do a redirect
        raise cherrypy.HTTPRedirect('/action/update?iss={}&tag={}'.format(
            qp[0], qp[1]))
Exemplo n.º 22
0
    def kill(self, iss, tag, ev):
        uqp, qp = unquote_quote(iss, tag)
        _key = self.app.assigned_ports.make_key(*uqp)

        try:
            pid = isrunning(unquote_plus(iss), unquote_plus(tag))
        except KeyError:
            pass
        else:
            if pid:
                # logger.info('kill {}'.format(pid))
                # subprocess.call(['kill', str(pid)])
                kill_process(pid)
                try:
                    del self.app.running_processes[_key]
                except KeyError:
                    pass
Exemplo n.º 23
0
    def kill(self, iss, tag, ev):
        uqp, qp = unquote_quote(iss, tag)
        _key = self.app.assigned_ports.make_key(*uqp)

        try:
            pid = isrunning(unquote_plus(iss), unquote_plus(tag))
        except KeyError:
            pass
        else:
            if pid:
                # logger.info('kill {}'.format(pid))
                # subprocess.call(['kill', str(pid)])
                kill_process(pid)
                try:
                    del self.app.running_processes[_key]
                except KeyError:
                    pass
Exemplo n.º 24
0
    def create(self, **kwargs):
        logger.info(
            'create test tool configuration: {} {}'.format(kwargs['iss'],
                                                           kwargs['tag']))

        uqp, qp = unquote_quote(kwargs['iss'], kwargs['tag'])
        if not uqp[0].startswith('https://') and not uqp[0].startswith('http://'):
            err = 'issuer value must start with "https://" or "http://"'
            logger.error(err)
            return as_bytes('Sorry failed to create: {}'.format(err))

        # construct profile
        try:
            profile = to_profile(kwargs)
        except KeyError as err:
            logger.error(err)
            return as_bytes('Sorry failed to create: {}'.format(err))

        _ent_conf = create_model(profile, ent_info_path=self.ent_info_path)

        if not do_discovery(profile):
            _ent_conf['client']['provider_info']['issuer'] = kwargs['iss']

        if not do_registration(profile):
            # need to create a redirect_uri, means I need to register a port
            _port = self.app.assigned_ports.register_port(kwargs['iss'],
                                                          kwargs['tag'])
            if self.app.test_tool_base.endswith('/'):
                _base = self.app.test_tool_base[:-1]
            else:
                _base = self.app.test_tool_base
            _ent_conf['client']['registration_response'][
                'redirect_uris'] = '[ "{}:{}/authz_cb", "{}:{}/authz_post" ]'.format(_base, _port, _base, _port)

        _ent_conf['tool']['issuer'] = uqp[0]
        _ent_conf['tool']['tag'] = uqp[1]
        _ent_conf['tool']['profile'] = profile

        _ent_conf.update(from_profile(profile))
        logger.info("Test tool config: {}".format(_ent_conf))

        self.rest.write(qp[0], qp[1], _ent_conf)
        # Do a redirect
        raise cherrypy.HTTPRedirect(
            '/action/update?iss={}&tag={}'.format(qp[0], qp[1]))
Exemplo n.º 25
0
    def read_conf(self, qiss, qtag):
        """

        :param qiss: OP issuer qoute_plus converted
        :param qtag: test instance tag quote_plus converted
        :return: Returns the instance configuration as a dictionary
        """
        uqp, qp = unquote_quote(qiss, qtag)
        logger.info('Read config: iss="{}", tag="{}"'.format(*uqp))

        fname = self.entity_file_name(*qp)
        if fname:
            if not os.path.isfile(fname):
                if qp[0].endswith('%2F'):  # try to remove
                    qp[0] = qp[0][:-3]
                else:  # else add
                    qp[0] += '%2F'
                fname = self.entity_file_name(qp[0], qp[1])
                if not os.path.isfile(fname):
                    logger.error('No such file')
                    raise NoSuchFile(fname)
                    # return self.list_dir(fname, qiss)

            try:
                _data = open(fname, 'r').read()
            except Exception as err:
                if sys.version[0] == '2':
                    if isinstance(err, IOError):
                        return None
                    else:
                        raise NoSuchFile(fname)
                elif isinstance(err, FileNotFoundError):
                    return None
                else:
                    logger.error('Unable to read from file: {}'.format(fname))
                    raise NoSuchFile(fname)
            try:
                return 'json', json.loads(_data)
            except Exception as err:
                logger.error(err)
                return None
        else:
            return None
Exemplo n.º 26
0
    def restart_instance(self, iss, tag, action='restart'):
        uqp, qp = unquote_quote(iss, tag)
        logger.info('{} iss="{}", tag="{}"'.format(action, uqp[0], uqp[1]))
        url = self.app.run_test_instance(qp[0], qp[1])

        if isinstance(url, Response):
            return conv_response(None, url)

        if url:
            args = {
                'title': "Action performed", 'base': self.baseurl,
                'note': 'Your test instance "{iss}:{tag}" has been '
                        '{act} as <a href="{url}">{url}</a>'.format(
                    iss=uqp[0], tag=uqp[1], url=url, act=action)}
        else:
            args = {
                'title': "Action Failed", 'base': self.baseurl,
                'note': 'Could not {} your test instance'.format(action)}

        _msg = self.html['message.html'].format(**args)
        return as_bytes(_msg)
Exemplo n.º 27
0
    def restore(self, iiss, itag):
        uqp, qp = unquote_quote(iiss, itag)
        logger.info('Restore iss="{}", tag="{}"'.format(*uqp))
        bname = '{}.{}'.format(qp[0], qp[1])
        last = 0.0
        last_backup = None
        for item in os.listdir(self.backup):
            if item.startswith("."):
                continue
            if not itag.startswith(bname):
                continue

            # Should be 3 parts, only interested in the last
            p = item.split('.')[-1]
            if float(p) > last:
                last = float(p)
                last_backup = item

        if last_backup:
            fn = os.path.join(self.backup, last_backup)
            info = open(fn, 'r').read()
            return self.rest.store(qp[0], qp[1], info)
Exemplo n.º 28
0
    def delete(self, iss, tag, ev, pid=0):
        logger.info('delete test tool configuration: {} {}'.format(iss, tag))

        # If already running - kill
        self.kill(iss, tag, ev)

        uqp, qp = unquote_quote(iss, tag)
        _key = self.app.assigned_ports.make_key(*uqp)

        os.unlink(os.path.join(self.entpath, *qp))
        # Remove issuer if out of tags
        if not os.listdir(os.path.join(self.entpath, qp[0])):
            os.rmdir(os.path.join(self.entpath, qp[0]))

        try:
            del self.app.assigned_ports[_key]
        except KeyError:  # How could it already have gone ? Ah, well
            pass

        # redirect back to entity page
        loc = '{}entity'.format(self.rest.base_url)
        raise cherrypy.HTTPRedirect(loc)
Exemplo n.º 29
0
    def delete(self, iss, tag, ev, pid=0):
        logger.info('delete test tool configuration: {} {}'.format(iss, tag))

        # If already running - kill
        self.kill(iss, tag, ev)

        uqp, qp = unquote_quote(iss, tag)
        _key = self.app.assigned_ports.make_key(*uqp)

        os.unlink(os.path.join(self.entpath, *qp))
        # Remove issuer if out of tags
        if not os.listdir(os.path.join(self.entpath, qp[0])):
            os.rmdir(os.path.join(self.entpath, qp[0]))

        try:
            del self.app.assigned_ports[_key]
        except KeyError:  # How could it already have gone ? Ah, well
            pass

        # redirect back to entity page
        loc = '{}entity'.format(self.rest.base_url)
        raise cherrypy.HTTPRedirect(loc)
Exemplo n.º 30
0
    def restore(self, iiss, itag):
        uqp, qp = unquote_quote(iiss, itag)
        logger.info('Restore iss="{}", tag="{}"'.format(*uqp))
        bname = '{}.{}'.format(qp[0], qp[1])
        last = 0.0
        last_backup = None
        for item in os.listdir(self.backup):
            if item.startswith("."):
                continue
            if not itag.startswith(bname):
                continue

            # Should be 3 parts, only interested in the last
            p = item.split('.')[-1]
            if float(p) > last:
                last = float(p)
                last_backup = item

        if last_backup:
            fn = os.path.join(self.backup, last_backup)
            info = open(fn, 'r').read()
            return self.rest.store(qp[0], qp[1], info)
Exemplo n.º 31
0
    def stop(self, iss, tag, ev):
        logger.info('stop test tool')

        uqp, qp = unquote_quote(iss, tag)
        _key = self.app.assigned_ports.make_key(*uqp)

        # If already running - kill
        try:
            pid = isrunning(unquote_plus(iss), unquote_plus(tag))
        except KeyError:
            pass
        else:
            if pid:
                #logger.info('kill {}'.format(pid))
                #subprocess.call(['kill', str(pid)])
                kill_process(pid)
                try:
                    del self.app.running_processes[_key]
                except KeyError:
                    pass

        # redirect back to entity page
        loc = '{}entity/{}'.format(self.rest.base_url, qp[0])
        raise cherrypy.HTTPRedirect(loc)