123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- #啟動和管理項目
- from distutils.log import error
- from encodings import utf_8
- from multiprocessing import Value
- from app import create_app
- from flask import render_template, request, session
- import paramiko
- import git
- import shutil
- from time import sleep as sl
- from threading import Lock
- import threading
- from concurrent.futures import ThreadPoolExecutor
- import csv
- import os
- import stat
- import time
- # app, db, mqtt, socketio = create_app() # Rita 0428
- app, db, mqtt = create_app()
- thread = None
- thread_lock = Lock()
- # Rita 0428
- # @socketio.on('connect_event', namespace='/test_conn')
- # def connected_msg(msg):
- # print("connect success")
- # print(msg)
- # socketio.emit('stdout', 'success', namespace='/test_conn')
- # print(socketio)
- D = {"item_name":0, "command":0}
- # 0411 shutil-rmtree 刪除 .get 唯讀檔
- # https://stackoverflow.com/questions/21261132/shutil-rmtree-to-remove-readonly-files
- def remove_readonly(func, path, _):
- os.chmod(path, stat.S_IWRITE)
- func(path)
-
- # # 建置伺服器的訪問路徑 -- 舊 保留 0413 測試
- # @app.route('/add_server', methods=['POST', 'GET'])
- # def add_server_views():
- # username = session['uname']
- # L = []
- # if request.method == 'GET':
- # # 0407 測試註解一下
- # # repo = git.Repo.init(path='.')
- # # new_repo = git.Repo.clone_from(url='http://60.250.156.230:3000/benson/TestSSH.git', to_path='../new')
- # # new_repo = git.Repo.clone_from(url='http://60.250.156.230:3000/rita/CoffeeProject.git', to_path='../new')
- # os.system('git clone -b sh --single-branch http://60.250.156.230:3000/Automatic_build_system/Automatically_install_files.git C:\\Users\\USER\\Rita\\operating\\sixthproject\\new')
- # # with open('/home/benson/Project/new/All_installv3.2.sh') as f:
- # with open('../new/coffeesystem.sh', encoding='utf-8') as f:
- # # content = f.read().split('-Dividing line-')
- # # D['item_name'] = content[0].split('\n')[1:]
- # # D['command'] = content[1].split('\n')
- # D['command'] = f.read().split('\n')
- # # print("D['command']: ", D['command'])
- # # # 0407 測試中暫時註解
- # # # with open('/home/benson/Project/new/Item.txt') as f:
- # # with open('../new/Item.txt') as f:
- # # D['item_name'] = f.read().split('\n')
- # # for item in D['item_name']:
- # # L.append('.'.join(item.split('.')[1:])) # 拿掉 '.'.join 的話會變成 [['python3', '8'], ['python3-pip'],
-
-
- # # os.remove('C:/Users/USER/Rita/operating/sixthproject/new/.git')
- # # os.system('rm -rf ../new/.git')
- # # os.chmod('../new/.git', stat.S_IWRITE)
- # # os.unlink('../new/.git')
- # # sl(6)
- # # shutil.rmtree('../new', ignore_errors=True, onerror=del_rw)
- # sl(3)
- # shutil.rmtree('../new', onerror=remove_readonly)
- # sl(3)
- # # os.rmdir('../new')
- # # sl(6)
- # # #刪除非空資料夾
- # # try:
- # # shutil.rmtree('../new', ignore_errors=True, onerror=del_rw)
- # # # os.remove('../new')
- # # print("=== DELETE ===")
- # # sl(1)
- # # except OSError as e:
- # # print(f"OSError:{e.strerror}") # Error:存取被拒。
- # # # print("L: ", L) # L: ['python3.8', 'python3-pip', 'git', ... , 'ROS-ubuntu-18.04-melodic']
-
- # # 0407 測試註解一下
- # # for i in range(0, len(D['item_name'])):
- # # item = D['item_name'][i].split('=')[-1].replace('"','')
- # # L.append(item)
- # # L = L[:-1]
- # # # 0411 測試
- # # D['command'] = [
- # # # 'cd /tmp/; pwd; sudo wget https://files.phpmyadmin.net/phpMyAdmin/5.1.0/phpMyAdmin-5.1.0-all-languages.tar.gz;tar -zxvf phpMyAdmin-5.1.0-all-languages.tar.gz;sudo rm phpMyAdmin-5.1.0-all-languages.tar.gz; sudo mv phpMyAdmin-5.1.0-all-languages /var/www/html/phpmyadmin;',
- # # 'cd /var/www/html/phpmyadmin; pwd; mkdir tmp;',
- # # 'cd /var/www/html/phpmyadmin; pwd; mkdir tmp;',
- # # 'cd; pwd;',
- # # 'pwd']
- # L2= []
- # for i in range(0, len(D['command'])):
- # if D['command'][i] == "'" or D['command'][i] == '' or "#" in D['command'][i] or D['command'][i] == ' ':
- # continue
- # L2.append(D['command'][i])
- # D['command'] = L2
- # # print("D['command'] = L2: ", D['command'])
- # # print("=============== D['item_name'] ===============")
- # # print(D['item_name'])
- # # print("=============== D['command'] ===============")
- # # print(D['command'])
- # # print("=============== CODE CHECK 3 ===============")
- # # content = D['item_name'] + D['command']
- # # for i in content:
- # # print(i)
- # return render_template('add_server.html', params=locals())
- # else:
- # item_install_fail = 0
- # d = request.form.to_dict()
- # ip = d['ip']
- # loginname = d['loginname']
- # username = d['username']
- # password = d['password']
- # print(ip)
- # print(loginname)
- # print(username)
- # print(password)
- # try:
- # # 建立一個sshclient物件
- # ssh = paramiko.SSHClient()
- # # 允許將信任的主機自動加入到host_allow 列表,此方法必須放在connect方法的前面
- # ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- # # 指定本地的RSA私鑰檔案,如果建立金鑰對時設定的有密碼,password為設定的密碼,如無不用指定password引數
- # # pkey = paramiko.RSAKey.from_private_key_file('/home/super/.ssh/id_rsa', password='12345')
- # # pkey = paramiko.RSAKey.from_private_key_file('/home/ptop/topicjie/scripts/keys/id_rsa')
- # # 建立連線
- # # ssh.connect(hostname=ip,
- # # port=22,
- # # username='gs1801',
- # # pkey=pkey)
- # # with open('/home/benson/test11.sh') as f:
- # # content = f.read().split('\n')[0:-1]
- # # print(content)
- # # L = []
- # # for i in range(0, len(D['ssh_content'])):
- # # if "#" in D['ssh_content'][i] or D['ssh_content'][i] == '' or D['ssh_content'][i] == ' ':
- # # continue
- # # L.append(D['ssh_content'][i])
- # # for i in L:
- # # print(i)
- # ssh.connect(hostname=ip,
- # port=22,
- # username=loginname,
- # password=password)
- # # 假如帳號密碼正確沒有被捕捉到異常,傳送訊息給前端
- # socketio.emit('user-confirm', 'pass', namespace='/test_conn')
- # # os.system("scp /home/benson/test10.sh gs1801@" + ip + ":/home/gs1801/")
- # # 執行命令
- # # stdin, stdout, stderr = ssh.exec_command("ls -l /home/gs1801;touch test7.py")
- # # stdin, stdout, stderr = ssh.exec_command("touch test10.sh;echo '" + content + "' >> test10.sh;bash test10.sh")
- # # print('test1')
- # # print(stdout.read().decode())
- # # print('test2')
- # # print(stderr.read().decode())
- # # content = D['item_name'] + D['command']
- # with open('ItemFile.csv', 'w', newline='') as csvFile:
- # csvWriter = csv.writer(csvFile)
- # csvWriter.writerow(['Command', 'Install Content', 'Time', 'Status'])
- # # # Rita 0406 TEST
- # D['command'] = [
- # # 'sudo pip3 install cryptography==3.3.2
- # ]
- # for command in D['command']:
- # if '使用者' in command :
- # command = command.replace('使用者', username)
- # print(command)
- # stdin, stdout, stderr = ssh.exec_command(command)
- # command_start_time = time.time()
- # # sl(6) # Rita 原本為 sl(6) 增加後觀察是否 clone 輸出會改變
- # # Rita 0406 stdout(success)、stderr(error) 每次只會輸出其中一個, 以此做為判斷, 例如:
- # # print('=========================================')
- # # print('stdout: ', stdout.read().decode())
- # # print('stderr: ', stderr.read().decode())
- # # print('=========================================')
- # # 輸出為:
- # # =========================================
- # # stdout: 正在讀取套件清單...
- # # 正在重建相依關係...
- # # 正在讀取狀態資料...
- # # python3.8 is already the newest version (3.8.0-3ubuntu1~18.04.2).
- # # 升級 0 個,新安裝 0 個,移除 0 個,有 208 個未被升級。
- # # stderr:
- # # =========================================
-
- # error = stderr.read().decode()
- # success = stdout.read().decode()
- # # print('error: ', error, '\nlen(error): ', len(error))
- # print('error: ', error, '\nlen(error): ')
- # print('success: ', success, '\nlen(success): ')
- # if len(success) > 200000:
- # success = success[0:20000] + '... etc'
- # execution_time = round(time.time() - command_start_time, 3)
- # if error:
- # print('error')
- # print(error)
- # if item_install_fail == 0:
- # item_install_fail = 1
- # with open('ItemFile.csv', 'a', newline='') as csvFile:
- # csvWriter = csv.writer(csvFile)
- # csvWriter.writerow([command.replace(';', ';\n'),
- # 'success: \n' + success + '\n' + 'error: \n' + error,
- # # 'success: \n' + success,
- # # 'error: \n' + error,
- # execution_time,
- # 'error'])
- # socketio.emit('stderr', error, namespace='/test_conn')
- # else:
- # print('success')
- # print(success)
- # with open('ItemFile.csv', 'a', newline='') as csvFile:
- # csvWriter = csv.writer(csvFile)
- # csvWriter.writerow([command.replace(';', ';\n'),
- # 'success: \n' + success + '\nerror: \n' + error,
- # execution_time,
- # 'success'])
- # socketio.emit('stdout', success, namespace='/test_conn')
- # sl(1)
- # print('-----------------------------------------------------------------------------')
- # # Rita 0406 本來就註解
- # # def sshJob():
- # # for command in content:
- # # stdin, stdout, stderr = ssh.exec_command(command)
- # # #
- # # # # print(stdout.read().decode())
- # # # # print('----------------------------------------')
- # # # # print(stderr.read().decode())
- # # #
- # # print(stdin)
- # # if stderr.read().decode():
- # # L.append(0)
- # # print('error')
- # # socketio.emit('stderr', stderr.read().decode())
- # # else:
- # # L.append(1)
- # # print('success')
- # # socketio.emit('stdout', stdout.read().decode())
- # # sl(5)
- # # ssh.close()
- # # Rita 0406 本來就註解
- # # def sshJob():
- # # sl(5)
- # # i = 0
- # # total = len(content)
- # # while True:
- # # stdin, stdout, stderr = ssh.exec_command(content[i])
- # # #
- # # # # print(stdout.read().decode())
- # # # # print('----------------------------------------')
- # # # # print(stderr.read().decode())
- # # #
- # # if stderr.read().decode():
- # # L.append(0)
- # # print('error')
- # # socketio.emit('stderr', stderr.read().decode())
- # # else:
- # # L.append(1)
- # # print('success')
- # # socketio.emit('stdout', stdout.read().decode())
- # # sl(5)
- # # i += 1
- # # if i == total:
- # # break
- # # ssh.close()
- # # ssh_thread = threading.Thread(target=sshJob)
- # # ssh_thread.daemon = True
- # # ssh_thread.start()
- # # global thread
- # # with thread_lock:
- # # if thread is None:
- # # thread = socketio.start_background_task(target=sshJob)
- # # print(L)
- # #
- # # item_list = ItemList()
- # # item_list.ip = ip
- # # item_list.pymysql = L[0]
- # # item_list.eventlet = L[1]
- # # datetime = dt.now()
- # # item_list.datetime = datetime
- # # db.session.add(item_list)
- # # db.session.commit()
- # # 關閉寫入的 CSV 檔案
- # csvFile.close()
- # # 關閉連線
- # ssh.close()
- # if item_install_fail:
- # res = '有套件未安裝完成!'
- # else:
- # res = '建置完成!'
- # return res
- # except Exception as e:
- # print(e)
- # return str(e)
- Automatic_Build_System = {
- 'python3.8': '0',
- 'python3-pip': '0',
- 'cryptography': '0',
- 'upgrade': '0',
- 'grep': '0',
- 'git': '0',
- 'gitpython': '0',
- 'python3-tk': '0',
- 'wget': '0',
- 'python3-dev': '0',
- 'default-libmysqlclient-dev': '0',
- 'build-essential': '0',
- 'tasksel': '0',
- 'lamp-server': '0',
- 'phpMyAdmin-5.1.0-all-languages.tar.gz': '0',
- 'openssl': '0',
- 'php-common': '0',
- 'php-curl': '0',
- 'php-json': '0',
- 'php-mbstring': '0',
- 'php-mysql': '0',
- 'php-xml': '0',
- 'php-zip': '0',
- 'php7.2-mbstring': '0',
- 'php7.2-xml': '0',
- 'php7.2-mysql': '0',
- 'php7.2-common': '0',
- 'php7.2-curl': '0',
- 'php7.2-json': '0',
- 'php7.2-zip': '0',
- 'apache2': '0',
- 'paho-mqtt': '0',
- 'mosquitto': '0',
- 'mqtt_server_setting': '0',
- 'rclocal/rc.local': '0',
- 'golang': '0',
- 'gogsfile': '0',
- 'requirements': '0',
- 'CoffeeProject': '0',
- 'BBend-Test-JWT': '0',
- 'Coffee_MySQL': '0',
- 'chrony': '0',
- 'ntpdate': '0',
- '--keyserver': '0',
- 'update': '0',
- 'ros-melodic-desktop-full': '0',
- 'ros-melodic-rqt*': '0',
- 'python-rosdep': '0',
- 'python-rosinstall': '0',
- 'python-rosinstall-generator': '0',
- 'python-wstool': '0',
- 'catkin_make': '0'
- }
- @app.route('/ABS_update', methods=['POST', 'GET'])
- def ABS_update():
- return Automatic_Build_System
- # 0413 測試中, 原版在上方
- @app.route('/add_server', methods=['POST', 'GET'])
- def add_server_views():
- username = session['uname']
- L = []
- CURRENT_PATH = os.path.dirname(__file__)
- # C:\Users\USER\Rita\operating\sixthproject\BBend-Test-JWT 同 manage.py 路徑
- print("CURRENT_PATH: ", CURRENT_PATH)
-
- if request.method == 'GET':
- with open(CURRENT_PATH + '/Item.txt') as f:
- D['item_name'] = f.read().split('\n')
- for item in D['item_name']:
- # L.append('.'.join(item.split('.')[1:])) # 拿掉 '.'.join 的話會變成 [['python3', '8'], ['python3-pip'],
- L.append(item)
- # STATUS = session['status']
- return render_template('add_server.html', params=locals())
- else:
- item_install_fail = 0
- d = request.form.to_dict()
- ip = d['ip']
- loginname = d['loginname']
- username = d['username']
- password = d['password']
- item1 = d['item1_checked']
- item2 = d['item2_checked']
- item3 = d['item3_checked']
- item4 = d['item4_checked']
- item5 = d['item5_checked']
- item6 = d['item6_checked']
- item7 = d['item7_checked']
- item8 = d['item8_checked']
- print(d)
- try:
- # Rita 0428 測試
- # 建立一個sshclient物件
- ssh = paramiko.SSHClient()
- # 允許將信任的主機自動加入到host_allow 列表,此方法必須放在connect方法的前面
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- # 指定本地的RSA私鑰檔案,如果建立金鑰對時設定的有密碼,password為設定的密碼,如無不用指定password引數
- # pkey = paramiko.RSAKey.from_private_key_file('/home/super/.ssh/id_rsa', password='12345')
- # pkey = paramiko.RSAKey.from_private_key_file('/home/ptop/topicjie/scripts/keys/id_rsa')
- # 建立連線
- ssh.connect(hostname=ip,
- port=22,
- username=loginname,
- password=password)
- # 假如帳號密碼正確沒有被捕捉到異常,傳送訊息給前端
- # socketio.emit('user-confirm', 'pass', namespace='/test_conn')
- with open(CURRENT_PATH + '/ItemFile.csv', 'w', newline='') as csvFile:
- csvWriter = csv.writer(csvFile)
- csvWriter.writerow(['Command', 'Install Content', 'Time', 'Status'])
-
- for item_num in range(1, 9):
- # if item1 == 'true':
- if eval("item" + str(item_num)) == 'true':
- print('========== item ' + str(item_num) + ' install ====================================')
- # 下載資料夾
- os.system('git clone -b item' + str(item_num) + ' --single-branch http://60.250.156.230:3000/Automatic_build_system/Automatically_install_files.git ' + CURRENT_PATH + '/item' + str(item_num) + '')
- with open( CURRENT_PATH + '/item' + str(item_num) + '/item' + str(item_num) + '.sh', encoding='utf-8') as f:
- D['command'] = f.read().split('\n')
- sl(1)
- # 刪除下載資料夾
- shutil.rmtree( CURRENT_PATH + '/item' + str(item_num) + '', onerror=remove_readonly)
-
- # 移除不需要執行之指令
- sl(1)
- L2= []
- for i in range(0, len(D['command'])):
- if D['command'][i] == "'" or D['command'][i] == '' or "#" in D['command'][i] or D['command'][i] == ' ':
- continue
- L2.append(D['command'][i])
- D['command'] = L2
- # 0510 Rita 測試
- for command in D['command']:
- if '使用者' in command :
- command = command.replace('使用者', username)
- if 'sudo apt-get install' in command :
- command = command.replace('sudo apt-get install', "sudo DEBIAN_FRONTEND=noninteractive apt-get install")
- print(command)
- # 判斷若此 command 符合 Automatic_Build_System 的 Key, 則改變狀態
- for key, value in Automatic_Build_System.items():
- if key in command and value == '0':
- # print('key: ', key, ' / command: ', command)
- Automatic_Build_System[key] = "Install"
- sl(1)
- command_start_time = time.time()
- # 0504 Rita 測試
- stdin, stdout, stderr = ssh.exec_command(command)
- error = stderr.read().decode()
- success = stdout.read().decode()
- # print('error: ', error, '\nlen(error): ', len(error))
- print('error: ', error)
- print('success: ', success)
- if len(success) > 200000:
- success = success[0:20000] + '... etc'
-
- execution_time = round(time.time() - command_start_time, 3)
- if error:
- # 排除 錯誤訊息中的 警告訊息
- Warning_list = [
- "WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager.",
- "Cloning into",
- "Warning: apt-key output should not be parsed (stdout is not a terminal)",
- "gpg",
- "Warning: running 'rosdep update' as root is not recommended.",
- "You should run",
- "[Warning] Using a password on the command line interface can be insecure."
- ]
- error_command_list = error.split('\n')
- for_error_command_list = error_command_list.copy()
- for cl in range(0, len(error_command_list)):
- for W in range(len(Warning_list)):
- try:
- if Warning_list[W] in error_command_list[cl]:
- for_error_command_list.remove(error_command_list[cl])
- except IndexError:
- pass
- if for_error_command_list == [''] or for_error_command_list == []:
- print('=== Warning ===')
- with open(CURRENT_PATH + '/ItemFile.csv', 'a', newline='') as csvFile:
- csvWriter = csv.writer(csvFile)
- csvWriter.writerow([command.replace(';', ';\n'),
- 'success: \n' + success + '\n' + 'error: \n' + error,
- execution_time,
- 'warning'])
- # 判斷若此 command 符合 Automatic_Build_System 的 Key, 則改變狀態
- for key, value in Automatic_Build_System.items():
- if key in command:
- Automatic_Build_System[key] = execution_time
- else:
- print('for_error_command_list: ', for_error_command_list)
- print('=== error ===')
- if item_install_fail == 0:
- item_install_fail = 1
- with open(CURRENT_PATH + '/ItemFile.csv', 'a', newline='') as csvFile:
- csvWriter = csv.writer(csvFile)
- csvWriter.writerow([command.replace(';', ';\n'),
- 'success: \n' + success + '\n' + 'error: \n' + error,
- execution_time,
- 'error'])
- # 判斷若此 command 符合 Automatic_Build_System 的 Key, 則改變狀態
- for key, value in Automatic_Build_System.items():
- if key in command:
- Automatic_Build_System[key] = "Error"
- else:
- print('=== success ===')
- with open(CURRENT_PATH + '/ItemFile.csv', 'a', newline='') as csvFile:
- csvWriter = csv.writer(csvFile)
- csvWriter.writerow([command.replace(';', ';\n'),
- 'success: \n' + success + '\nerror: \n' + error,
- execution_time,
- 'success'])
- # 判斷若此 command 符合 Automatic_Build_System 的 Key, 則改變狀態
- for key, value in Automatic_Build_System.items():
- if key in command:
- Automatic_Build_System[key] = execution_time
-
- sl(5) # Rita 原本為 sl(6) 增加後觀察是否 clone 輸出會改變
- print('-----------------------------------------------------------------------------')
-
- # stdin, stdout, stderr = ssh.exec_command("sudo systemctl restart rc-local.service")
- sl(5)
- # stdin, stdout, stderr = ssh.exec_command("sudo reboot")
- with open( CURRENT_PATH + '/ItemFile.csv', 'a', newline='') as csvFile:
- csvWriter = csv.writer(csvFile)
- csvWriter.writerow(['',
- 'Automatic_Build_System: \n' + str(Automatic_Build_System),
- '',
- ''])
- # 關閉連線
- ssh.close()
- if item_install_fail:
- res = '有套件未安裝完成!'
- else:
- res = '建置完成!'
- return res
- except Exception as e:
- print(e)
- return str(e)
- #mqtt發布
- @app.route('/mqtt', methods=['GET','POST'])
- def mqtt_views():
- import json
- dict = request.args.to_dict()
- json = json.dumps(dict)
- print(json)
- mqtt.publish('AISKY/AppleFarm/MK-G/b8:27:eb:f8:24:92', json)
- return "Publish done"
- #mqtt訂閱
- @mqtt.on_connect()
- def handle_connect(client, userdata, flags, rc):
- mqtt.subscribe('AISKY/AppleFarm/MK-G/b8:27:eb:f8:24:92/Log')
- #處理mqtt訂閱的信息
- @mqtt.on_message()
- def handle_mqtt_message(client, userdata, message):
- payload = message.payload.decode()
- # p = json.loads(payload)
- # print("-------msg-------")
- # print('name :', p['name'])
- # print('email :', p['email'])
- print(payload)
- #調用日誌訊息
- @mqtt.on_log()
- def handle_logging(client, userdata, level, buf):
- print(level, buf)
- #自動關閉所有未使用、掛著的連接
- @app.teardown_appcontext
- def shutdown_session(exception=None):
- db.session.remove()
- if __name__ == '__main__':
- # Rita 0406 socketio.run(app, debug=True, host='0.0.0.0', port=5014)
- # Rita 0406 app.run(debug=True, host='0.0.0.0', port=5014)
- # print("===== BBend =====")
- # socketio.run(app, debug=True, host='0.0.0.0', port=5014)
- app.run(debug=True, host='0.0.0.0', port=5014)
|