diff --git a/locust/configuration.py b/locust/configuration.py index 9e114212b3..4ab288dbd8 100644 --- a/locust/configuration.py +++ b/locust/configuration.py @@ -5,37 +5,7 @@ from flask import make_response logger = logging.getLogger(__name__) -config_path = '/tests/settings/config.json' - -def read_file(): - """ - Will read the file and return it as a string with tree view. - """ - try: - with open((os.environ['PYTHONPATH'].split(os.pathsep))[-1] + config_path, "r") as data_file: - data = data_file.read() - except Exception as err: - logger.info(err) - data = "{}" - return data - -def write_file(string_json): - """ - The `string_json` will overwrite existing configuration. - If the previous configuration doesn't exist, then it will create the file. - """ - status, message = None, None - try: - with open((os.environ['PYTHONPATH'].split(os.pathsep))[-1] + config_path, "w") as data_file: - data_file.write(string_json) - status = True - message = 'Configuration has been saved' - events.master_new_configuration.fire(new_config=string_json) - except Exception as err: - logger.info(err) - status = False - message = "Can't save the configuration :" + err - return status, message +CONFIG_PATH = '/tests/settings/config.json' class ClientConfiguration: """ @@ -51,7 +21,7 @@ def read_json(self): """ if self.config_data is None: try: - with open((os.environ['PYTHONPATH'].split(os.pathsep))[-1] + config_path, "r") as data_file: + with open((os.environ['PYTHONPATH'].split(os.pathsep))[-1] + CONFIG_PATH, "r") as data_file: self.config_data = json.load(data_file) except Exception as err: logger.info(err) diff --git a/locust/events.py b/locust/events.py index d805f7012e..dc310dc863 100644 --- a/locust/events.py +++ b/locust/events.py @@ -135,3 +135,8 @@ def fire(self, **kwargs): """ *master_new_configuration* is fired when new configuration saved from master """ + +master_new_file_uploaded = EventHook() +""" +*master_new_file_uploaded* is fired when user upload a new test file +""" \ No newline at end of file diff --git a/locust/fileio.py b/locust/fileio.py new file mode 100644 index 0000000000..ed673a5183 --- /dev/null +++ b/locust/fileio.py @@ -0,0 +1,30 @@ +import logging +import os +import events +import werkzeug + +logger = logging.getLogger(__name__) + +def read(uploaded_file_path): + try: + with open(os_path()+uploaded_file_path, "r") as data_file: + data = data_file.read() + except IOError as err: + logger.info("error read: " + err.strerror) + data = None + return data + +def write(file_path, file_content): + try: + with open(os_path()+file_path, "w") as data_file: + data_file.write(file_content) + status = True + message = 'File has been saved' + except IOError as err: + logger.info("error write: " + err.strerror ) + status = False + message = "Can't overwrite protected file. Please rename your test file. i.e : home_production_dilan.py" + return status, message + +def os_path(): + return os.environ['PYTHONPATH'].split(os.pathsep)[-1]+os.sep diff --git a/locust/main.py b/locust/main.py index 13a4e01fe2..98c2599f99 100644 --- a/locust/main.py +++ b/locust/main.py @@ -10,6 +10,8 @@ import logging import socket import time +import tests_loader +import fileio from optparse import OptionParser from . import web @@ -261,137 +263,6 @@ def parse_options(): opts, args = parser.parse_args() return parser, opts, args - -def _is_package(path): - """ - Is the given path a Python package? - """ - return ( - os.path.isdir(path) - and os.path.exists(os.path.join(path, '__init__.py')) - ) - - -def find_locustfile(locustfile): - """ - Attempt to locate a locustfile, either explicitly or by searching parent dirs. - """ - # Obtain env value - names = [locustfile] - # Create .py version if necessary - if not names[0].endswith('.py'): - names += [names[0] + '.py'] - # Does the name contain path elements? - if os.path.dirname(names[0]): - # If so, expand home-directory markers and test for existence - for name in names: - expanded = os.path.expanduser(name) - if os.path.exists(expanded): - if name.endswith('.py') or _is_package(expanded): - return os.path.abspath(expanded) - else: - # Otherwise, start in cwd and work downwards towards filesystem root - path = '.' - # Stop before falling off root of filesystem (should be platform - # agnostic) - while os.path.split(os.path.abspath(path))[1]: - for name in names: - joined = os.path.join(path, name) - if os.path.exists(joined): - if name.endswith('.py') or _is_package(joined): - return os.path.abspath(joined) - path = os.path.join('..', path) - # Implicit 'return None' if nothing was found - - -def is_locust(tup): - """ - Takes (name, object) tuple, returns True if it's a public Locust subclass. - """ - name, item = tup - return bool( - inspect.isclass(item) - and issubclass(item, Locust) - and hasattr(item, "task_set") - and getattr(item, "task_set") - and not name.startswith('_') - ) - -def truncate_path(path): - # split path which comes from command on terminal - splitted_path = os.path.normpath(path).split(os.path.sep) - - count = 0 - for i in reversed(xrange(len(splitted_path))): - if count < 3 and splitted_path[i]: - if count == 0: - final_path = splitted_path[i] - elif count == 2: - final_path = os.path.join("...", splitted_path[i], final_path) - else: - final_path = os.path.join(splitted_path[i], final_path) - count += 1 - else: - break - return final_path - -def load_locustfile(path): - """ - Import given locustfile path and return (docstring, callables). - - Specifically, the locustfile's ``__doc__`` attribute (a string) and a - dictionary of ``{'name': callable}`` containing all callables which pass - the "is a Locust" test. - """ - # Get directory and locustfile name - directory, locustfile = os.path.split(path) - # If the directory isn't in the PYTHONPATH, add it so our import will work - added_to_path = False - index = None - if directory not in sys.path: - sys.path.insert(0, directory) - added_to_path = True - # If the directory IS in the PYTHONPATH, move it to the front temporarily, - # otherwise other locustfiles -- like Locusts's own -- may scoop the intended - # one. - else: - i = sys.path.index(directory) - if i != 0: - # Store index for later restoration - index = i - # Add to front, then remove from original position - sys.path.insert(0, directory) - del sys.path[i + 1] - # Perform the import (trimming off the .py) - imported = imp.load_source(os.path.splitext(locustfile)[0], path) - # Remove directory from path if we added it ourselves (just to be neat) - if added_to_path: - del sys.path[0] - # Put back in original index if we moved it - if index is not None: - sys.path.insert(index + 1, directory) - del sys.path[0] - # Return our two-tuple - locusts = dict(filter(is_locust, vars(imported).items())) - - # truncate the fullpath - final_path = truncate_path(path) - - return {final_path: locusts} - -def collect_locustfiles(path): - collected = dict() - - for root, dirs, files in os.walk(path): - if files: - for file_ in files: - if file_.endswith('.py') and not file_.endswith('__init__.py'): - fullpath = os.path.abspath(os.path.join(root, file_)) - loaded = load_locustfile(fullpath) - if loaded: - collected.update(loaded) - return collected - def main(): parser, options, arguments = parse_options() @@ -404,9 +275,9 @@ def main(): sys.exit(0) if os.path.isdir(options.locustfile): - all_locustfiles = collect_locustfiles(options.locustfile) + all_locustfiles = tests_loader.collect_locustfiles(options.locustfile) else: - locustfile = find_locustfile(options.locustfile) + locustfile = tests_loader.find_locustfile(options.locustfile) if not locustfile: logger.error("Could not find any locustfile! Ensure file ends in '.py' and see --help for available options.") @@ -417,7 +288,7 @@ def main(): sys.exit(1) # docstring, all_locustsfiles = load_locustfile(locustfile) - all_locustfiles = load_locustfile(locustfile) + all_locustfiles = tests_loader.load_locustfile(locustfile) # Use the first locustfile for the default locusts locusts = all_locustfiles.values()[0] diff --git a/locust/runners.py b/locust/runners.py index ae79ca319f..36cff9fd52 100644 --- a/locust/runners.py +++ b/locust/runners.py @@ -18,6 +18,9 @@ from .rpc import rpc, Message +import fileio +import tests_loader + logger = logging.getLogger(__name__) # global locust runner singleton @@ -203,6 +206,9 @@ def log_exception(self, node_id, msg, formatted_tb): row["count"] += 1 row["nodes"].add(node_id) self.exceptions[key] = row + + def reload_tests(self): + self.available_locustfiles = tests_loader.load(self.options.locustfile) class LocalLocustRunner(LocustRunner): def __init__(self, locust_classes, options, available_locustfiles=None): @@ -254,7 +260,6 @@ def hatching(self): @property def running(self): return self.get_by_state(STATE_RUNNING) - self.clients = SlaveNodesDict() self.server = rpc.Server(self.master_bind_host, self.master_bind_port) self.greenlet = Group() @@ -288,6 +293,13 @@ def on_master_new_configuration(new_config): self.server.send(Message("config", data, None)) events.master_new_configuration += on_master_new_configuration + def on_master_new_file_uploaded(new_file): + logger.info("master has been received a new test file, slaves should update theirs too") + self.reload_tests() + for client in six.itervalues(self.clients): + self.server.send(Message("python_file", new_file, None)) + events.master_new_file_uploaded += on_master_new_file_uploaded + @property def user_count(self): return sum([c.user_count for c in six.itervalues(self.clients)]) @@ -432,7 +444,15 @@ def worker(self): self.locust_classes = self.available_locustfiles[msg.data].values() elif msg.type == "config": logger.info("Got new config from master, updating this slave config") - configuration.write_file(msg.data['config']) + fileio.write(configuration.CONFIG_PATH, msg.data['config']) + events.master_new_configuration.fire(new_config=msg.data['config']) + elif msg.type == "python_file": + logger.info("Uploaded test file from master detected, writing now") + new_file = msg.data + upload_status,upload_message = fileio.write(new_file['full_path'], new_file['content']) + if upload_status is False : + logger.info("error while creating new file: " + upload_message) + self.reload_tests() def stats_reporter(self): diff --git a/locust/static/locust.js b/locust/static/locust.js index e9aeb0adb1..28e3b976e0 100644 --- a/locust/static/locust.js +++ b/locust/static/locust.js @@ -1,10 +1,19 @@ $(window).ready(function() { - $('.select2').select2({theme: 'bootstrap', width: "97.3%"}); + $('.select2').select2({theme: 'bootstrap', width: "100%"}); if($("#locust_count").length > 0) { $("#locust_count").focus().select(); } }); +$("#locustfile").on('select2:close', function(event){ + event.preventDefault(); + var el = $(this); + if(el.val()==="add_new_test_file") { + $('#add-new-file-modal').modal('show'); + } + +}); + $("#box_stop a.stop-button").click(function(event) { event.preventDefault(); $.get($(this).attr("href")); @@ -13,6 +22,7 @@ $("#box_stop a.stop-button").click(function(event) { $("a.new_test").show(); $("a.edit_test").hide(); $(".user_count").hide(); + $(".edit").hide(); }); $("#box_stop a.reset-button").click(function(event) { @@ -42,11 +52,7 @@ $(".ramp_test").click(function(event) { $("#new_test").click(function(event) { event.preventDefault(); - $("#start").show(); - $("#ramp").hide(); - $("#edit_config").hide(); - $("#locust_count").focus().select(); - $(".status").removeClass("none"); + $("#new-test-confirmation").modal('show'); }); $(".edit_test").click(function(event) { @@ -164,12 +170,45 @@ $(".edit_config_link").click(function(event) { }); +$('#upload_btn_submit').click(function(event){ + event.preventDefault(); + $('#upload_file_form').submit(); +}); + +$("#directories .select2").select2({ + placeholder: "Select a state" +}); + +$('#upload_file_form').submit(function(event) { + event.preventDefault(); + var form = $('#upload_file_form')[0]; + var form_data = new FormData(form); + $.ajax({ + type: 'POST', + url: "/upload_file", + enctype: "multipart/form-data", + contentType: false, + data: form_data, + cache: false, + processData: false, + success: function (response) { + if (response.success) { + location.reload(true); + } else { + alert(response.message); + } + } + }) +}); + + $('#submit_json_btn').click(function(){ event.preventDefault(); $('#hidden_config_json').val(JSON.stringify(json_editor.get(), null , 4)); $('#json_config_form').submit(); }); + $('#json_config_form').submit(function(event) { event.preventDefault(); $.post($(this).attr("action"), $(this).serialize(), @@ -181,6 +220,7 @@ $('#json_config_form').submit(function(event) { ); }); + $('#import_csv_btn').click(function(event) { event.preventDefault(); var form = $('#import_csv_form')[0]; @@ -339,12 +379,15 @@ $('#swarm_form').submit(function(event) { function(response) { if (response.success) { $("body").attr("class", "hatching"); - $("#start").fadeOut(); - $("#status").fadeIn(); - $(".box_running").fadeIn(); - $("a.new_test").fadeOut(); - $("a.edit_test").fadeIn(); - $(".user_count").fadeIn(); + $("#start").hide(); + $("#status").show(); + $(".box_running").show(); + $("a.new_test").hide(); + $("a.edit_test").show(); + $(".user_count").show(); + $(".menu").show(); + $("#stats").show(); + $(".box_running").show(); resetCharts(); } } diff --git a/locust/static/style.css b/locust/static/style.css index 73f3499c9f..20480eed75 100644 --- a/locust/static/style.css +++ b/locust/static/style.css @@ -302,8 +302,13 @@ label[for="json_option"]+div{ padding: 6px 15px 6px 0px !important; } +#add-new-file-modal .select2-selection--single { + width: 100%; +} + .start button, -.edit button, .edit_config button[type=submit], .multiple_column button { +.edit button, .edit_config button[type=submit], .multiple_column button, +#new-test-confirmation .btn-newtest, #upload_btn_submit { margin: 20px 0px 20px 0px; float: right; font-size: 14px; @@ -371,7 +376,6 @@ label[for="json_option"]+div{ .running a.new_test {display: none;} .stopped a.new_test {display: block;} - .start a.close_link, .edit a.close_link, .multiple_column a.close_link_headers { position: absolute; right: 10px; @@ -561,6 +565,70 @@ ul.tabs li a.current:after { overflow: hidden; } + +#add-new-file-modal, #new-test-confirmation { + width: 398px; + position: absolute; + left: 50%; + top: 130px; + height: 240px; + margin-left: -169px; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border: 3px solid #eee; + background: #132b21; + box-shadow: 0 0 120px rgba(0,0,0,0.3); +} +#add-new-file-modal{ + padding-left : 1%; + padding-right: 1%; + height: 275px; +} +#new-test-confirmation { + height: 160px; +} +#add-new-file-modal .modal-dialog, #new-test-confirmation .modal-dialog{ + display: flex; + flex-direction: column; + vertical-align: middle; + margin-top: 0px; + margin-bottom: -50px; +} +#add-new-file-modal .modal-header, #new-test-confirmation .modal-header { + border-bottom-color: transparent; +} +#add-new-file-modal .close, #new-test-confirmation .close { + color: #addf82; +} +#new-test-confirmation .modal-header .close, #add-new-file-modal .close{ + margin-right: -25px; + padding: 3px; +} + +#new-test-confirmation .modal-body, #add-new-file-modal .modal-body { + padding-top: 25px; + padding-bottom: 0px; + color: white; +} +#new-test-confirmation .modal-footer { + border-top-color: transparent; + padding: 0; +} +#new-test-confirmation .btn-newtest { + padding: 8px 30px; + margin: 3px 3px; +} +#add-new-file-modal .modal-content, #new-test-confirmation .modal-content { + background-color: #132b21; + border-color: transparent; +} +#add-new-file-modal .modal-title, #new-test-confirmation .modal-title { + color:#addf82; +} +#upload_file_form, #new-test-confirmation { + color: white; +} .about { width: 398px; position: absolute; @@ -579,7 +647,7 @@ ul.tabs li a.current:after { right: 10px; top: 10px; } -.about .padder { +.about .padder{ padding: 30px; padding-top: 0px; } @@ -610,6 +678,14 @@ ul.tabs li a.current:after { height: 100%; background: #172D24; } + +.center_padder { + position:relative; + padding-left: 350px; + width: 100%; + height: 100%; + background: #172D24; +} .chart-filter { top:90px; position: relative; @@ -687,4 +763,4 @@ label { .ramp .edit_config_link { position: relative; top: 36px; -} \ No newline at end of file +} diff --git a/locust/templates/index.html b/locust/templates/index.html index db2adf9e89..3ec82e899c 100644 --- a/locust/templates/index.html +++ b/locust/templates/index.html @@ -66,9 +66,6 @@
-
- Close -

Start new Locust swarm

@@ -80,12 +77,15 @@

Start new Locust swarm


- + + {% for name in available_locustfiles %} {% endfor %} -
+

Edit Config @@ -99,7 +99,55 @@

Start new Locust swarm

{% endif %}
- + +
Close diff --git a/locust/tests_loader.py b/locust/tests_loader.py new file mode 100644 index 0000000000..f0ee5f0b5b --- /dev/null +++ b/locust/tests_loader.py @@ -0,0 +1,163 @@ +import os +import sys +import imp +import inspect +import logging +import core + +def load(path): + logger = logging.getLogger(__name__) + + if os.path.isdir(path): + all_locustfiles = collect_locustfiles(path) + else: + locustfile = find_locustfile(path) + + if not locustfile: + logger.error("Could not find any locustfile! Ensure file ends in '.py' and see --help for available options.") + sys.exit(1) + + if locustfile == "locust.py": + logger.error("The locustfile must not be named `locust.py`. Please rename the file and try again.") + sys.exit(1) + + # docstring, all_locustsfiles = load_locustfile(locustfile) + all_locustfiles = load_locustfile(locustfile) + return all_locustfiles + +def collect_locustfiles(path): + collected = dict() + + for root, dirs, files in os.walk(path): + if files: + for file_ in files: + if file_.endswith('.py') and not file_.endswith('__init__.py'): + fullpath = os.path.abspath(os.path.join(root, file_)) + loaded = load_locustfile(fullpath) + if loaded: + collected.update(loaded) + return collected + +def populate_directories(os_path,working_dir): + directories = dict() + for root, subdirs, files in os.walk(os_path+working_dir): + directories.update({os.sep+working_dir:os.sep+working_dir}) + for subdir in subdirs: + directory = os.sep+os.path.join(root,subdir).replace(os_path,"")+os.sep + directories.update({directory:directory}) + return directories + +def load_locustfile(path): + """ + Import given locustfile path and return (docstring, callables). + + Specifically, the locustfile's ``__doc__`` attribute (a string) and a + dictionary of ``{'name': callable}`` containing all callables which pass + the "is a Locust" test. + """ + # Get directory and locustfile name + directory, locustfile = os.path.split(path) + # If the directory isn't in the PYTHONPATH, add it so our import will work + added_to_path = False + index = None + if directory not in sys.path: + sys.path.insert(0, directory) + added_to_path = True + # If the directory IS in the PYTHONPATH, move it to the front temporarily, + # otherwise other locustfiles -- like Locusts's own -- may scoop the intended + # one. + else: + i = sys.path.index(directory) + if i != 0: + # Store index for later restoration + index = i + # Add to front, then remove from original position + sys.path.insert(0, directory) + del sys.path[i + 1] + # Perform the import (trimming off the .py) + imported = imp.load_source(os.path.splitext(locustfile)[0], path) + # Remove directory from path if we added it ourselves (just to be neat) + if added_to_path: + del sys.path[0] + # Put back in original index if we moved it + if index is not None: + sys.path.insert(index + 1, directory) + del sys.path[0] + # Return our two-tuple + locusts = dict(filter(is_locust, vars(imported).items())) + + # truncate the fullpath + final_path = truncate_path(path) + + return {final_path: locusts} + +def find_locustfile(locustfile): + """ + Attempt to locate a locustfile, either explicitly or by searching parent dirs. + """ + # Obtain env value + names = [locustfile] + # Create .py version if necessary + if not names[0].endswith('.py'): + names += [names[0] + '.py'] + # Does the name contain path elements? + if os.path.dirname(names[0]): + # If so, expand home-directory markers and test for existence + for name in names: + expanded = os.path.expanduser(name) + if os.path.exists(expanded): + if name.endswith('.py') or _is_package(expanded): + return os.path.abspath(expanded) + else: + # Otherwise, start in cwd and work downwards towards filesystem root + path = '.' + # Stop before falling off root of filesystem (should be platform + # agnostic) + while os.path.split(os.path.abspath(path))[1]: + for name in names: + joined = os.path.join(path, name) + if os.path.exists(joined): + if name.endswith('.py') or _is_package(joined): + return os.path.abspath(joined) + path = os.path.join('..', path) + # Implicit 'return None' if nothing was found + +def truncate_path(path): + # split path which comes from command on terminal + splitted_path = os.path.normpath(path).split(os.path.sep) + + count = 0 + for i in reversed(xrange(len(splitted_path))): + if count < 3 and splitted_path[i]: + if count == 0: + final_path = splitted_path[i] + elif count == 2: + final_path = os.path.join("...", splitted_path[i], final_path) + else: + final_path = os.path.join(splitted_path[i], final_path) + count += 1 + else: + break + return final_path + +def is_locust(tup): + """ + Takes (name, object) tuple, returns True if it's a public Locust subclass. + """ + name, item = tup + return bool( + inspect.isclass(item) + and issubclass(item, core.Locust) + and hasattr(item, "task_set") + and getattr(item, "task_set") + and not name.startswith('_') + ) + +def _is_package(path): + """ + Is the given path a Python package? + """ + return ( + os.path.isdir(path) + and os.path.exists(os.path.join(path, '__init__.py')) + ) \ No newline at end of file diff --git a/locust/web.py b/locust/web.py index 10d428e576..130e1c79e3 100644 --- a/locust/web.py +++ b/locust/web.py @@ -1,14 +1,17 @@ # encoding: utf-8 -import csv, re, io, json, os.path +import csv, re, io, json, os.path, events from time import time from itertools import chain from collections import defaultdict from six.moves import StringIO, xrange import six +import main +import tests_loader from gevent import wsgi -from flask import Flask, make_response, request, render_template +from flask import Flask, make_response, request, render_template, redirect, url_for +from werkzeug.utils import secure_filename from . import runners, configuration from .cache import memoize @@ -16,7 +19,8 @@ from locust.stats import median_from_dict from locust import __version__ as version import gevent, itertools - +import fileio +import base64 import logging logger = logging.getLogger(__name__) @@ -55,17 +59,28 @@ def index(): else: edit_label = "" + pages = tests_loader.populate_directories(fileio.os_path(),'tests/pages/') + modules = tests_loader.populate_directories(fileio.os_path(),'tests/modules/') + modules.update(pages) + directories = modules + return render_template("index.html", state=runners.locust_runner.state, is_distributed=is_distributed, slave_count=slave_count, user_count=runners.locust_runner.user_count, available_locustfiles = sorted(runners.locust_runner.available_locustfiles.keys()), + test_file_directories = sorted(directories), version=version, ramp = _ramp, host=host ) +@app.route('/new', methods=["POST"]) +def newtest(): + runners.locust_runner.state = runners.STATE_INIT + return index() + @app.route('/swarm', methods=["POST"]) def swarm(): assert request.method == "POST" @@ -270,9 +285,10 @@ def ramp(): response.headers["Content-type"] = "application/json" return response + @app.route("/config/get_config_content", methods=["GET"]) def get_config_content(): - load_config = configuration.read_file() + load_config = fileio.read(configuration.CONFIG_PATH) response = make_response(json.dumps({'data':load_config})) response.headers["Content-type"] = "application/json" return response @@ -338,13 +354,35 @@ def convert_csv_to_json(): return response +@app.route("/upload_file", methods=["POST"]) +def upload_file(): + upload_directory = request.form.get('upload_directory') + python_file = request.files['python_file'] + python_file_path = upload_directory + python_file.filename + python_file_extension = os.path.splitext(python_file.filename)[1] + python_file_content = python_file.read() + if not python_file and python_file_extension != ".py": + return expected_response({'success':False, 'message':"Can't upload this file. Please try again with python file with .py extension"}) + upload_status,upload_message = fileio.write(python_file_path, python_file_content) + if upload_status is False : + return expected_response({'success':False, 'message':upload_message}) + events.master_new_file_uploaded.fire(new_file={"full_path": python_file_path, "name": python_file.filename, "content":python_file_content}) + runners.locust_runner.reload_tests() + return expected_response({'success':True, 'message':""}) + +def expected_response(json_dumps): + response = make_response(json.dumps(json_dumps)) + response.headers["Content-type"] = "application/json" + return response + @app.route("/config/save_json", methods=["POST"]) def save_json(): assert request.method == "POST" config_json = str(request.form["final_json"]) try: - success, message = configuration.write_file(config_json) + success, message = fileio.write(configuration.CONFIG_PATH, config_json) + events.master_new_configuration.fire(new_config=config_json) response = make_response(json.dumps({'success':success, 'message': message})) except Exception as err: response = make_response(json.dumps({'success':success, 'message': message}))