Search code examples
pythonflasktornadopingfile-sharing

Pinging a remote PC with Flask, causing server to block


I have an internal website, which is required to have file sharing links which are direct links to a shared location on the pc that the table row represents.

enter image description here

When accessing the links, I would like to first test if the remote pc is available, in the quickest possible fashion. I thought this would be a ping, but for some reason, timeout does not work with -w (yes windows)

This is not allowed to take time, for some reason, it causes the web server to block on ping, even though I am using Tornado to serve Flask routes asynchronously.

Preferably, I would like to have the server continously updating the front end, with active/deactive links, allowing users to only access links with pc's online, and restrict them elsehow. Possibly even maintaining the value in a database.

Any and all advice is welcome, I've never really worked with File Sharing before.

Backend is Python 3.4, Flask & Tornado.

The Ajax Call

function is_drive_online2(sender){
    hostname = sender.parentNode.parentNode.id;
   $.get('Media/test',{
        drive: hostname
        },
        function(returnedData){
            console.log(returnedData[hostname]);
            if(returnedData[hostname] == 0){
                open("file://"+hostname+"/MMUsers");
            }else{
                alert("Server Offline");
            }
        }
   );
}

The Response (Flask route)

@app.route('/Media/test', methods=['GET', 'POST'])
def ping_response():
    before = datetime.datetime.now()
    my_dict = dict()
    drive = request.args.get('drive')
    print(drive)
    response = os.system("ping -n 1 -w 1 " + drive)
    my_dict[drive] = response
    after = datetime.datetime.now()
    print(after-before)
    return json.dumps(my_dict), 200, {'Content-Type': 'application/json'}

The ping call takes 18 seconds to resolve, even with -w 1 (or 1000) enter image description here

I only need to support Internet Explorer 11. Is this even a plausible scenario? Are there hardware limitations to something like this Should the server have a long thread whose sole task is to continuously update active/deactivate links? I am not sure the best approach.

Thanks for reading.

EDIT 1:

Trying to apply the ping_response as native Tornado asynchronous response. Result is the same

class PingHandler(RequestHandler):
    @asynchronous
    def get(self):
        dr = self.get_argument('drive')
        print(dr)
        b = datetime.datetime.now()
        myreturn = {self.get_argument('drive'):
                    os.system("ping -n 1 -w 1 " + self.get_argument('drive'))}
        a = datetime.datetime.now()
        print(a-b)
        self.write(myreturn)

wsgi = WSGIContainer(app)
application = Application([(r"/Media/test", PingHandler),
                           (r".*", FallbackHandler, dict(fallback=wsgi))])


application.listen(8080)
IOLoop.instance().start()

EDIT 2: Trying to Use Celery. Still blocking.

def make_celery(app):
    celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
    celery.conf.update(app.config)
    TaskBase = celery.Task

    class ContextTask(TaskBase):
        abstract = True

        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)
    celery.Task = ContextTask
    return celery

celery = make_celery(app)


@celery.task
def ping(drive):
    """
    Background Task to test is computer is online
    :param drive: The drive name to test
    :return: Non Zero status code for Offline boxes.
    """

    response = os.system("ping -n 1 -w 1 " + drive)
    return json.dumps({drive: response}), 200, {'Content-Type': 'application/json'}


@app.route('/Media/test', methods=['GET', 'POST'])
def ping_response():
    before = datetime.datetime.now()
    my_dict = dict()
    drive = request.args.get('drive')
    print(drive)
    this_drive = temp_session.query(Drive).filter(Drive.name == drive).first()
    address = this_drive.computer.ip_address if this_drive.computer.ip_address else this_drive.name
    response = ping.apply_async(args=[address])

    return response

Solution

  • Tornado isn't serving your Flask app asynchronously (that's impossible: asynchronousness is a property of the interface and ping_response is a synchronous function). Tornado's WSGIContainer is a poor fit for what you're trying to do (see the warning in its docs)

    You should either use Flask with a multi-threaded server like gunicorn or uwsgi, or use native Tornado asynchronous RequestHandlers.