兜兜    2019-08-21 10:52:51    2019-11-14 14:25:36   

pygame NoVNC xvfb
### 流程 #### `浏览器--->noVNC--->xvfb虚拟桌面--->pygame程序` ### xvfb安装 #### 编辑Dockerfile ```bash cat Dockerfile ``` ```bash FROM ubuntu:xenial LABEL maintainer "sheyinsong" EXPOSE 5900 6099 ENV \ DEBIAN_FRONTEND="nonintractive" \ X11VNC_PASSWORD="password" RUN sed -i -e s#security.ubuntu.com#mirrors.aliyun.com#g -e s#archive.ubuntu.com#mirrors.aliyun.com#g /etc/apt/sources.list RUN apt-get update -y RUN apt-get install -y software-properties-common RUN add-apt-repository ppa:jonathonf/python-3.6 RUN apt-get update -y RUN apt-get install -y xvfb x11vnc python3.6 libglib2.0-0 RUN apt-get install -y curl RUN /bin/cp /usr/bin/python3.6 /usr/bin/python RUN curl https://bootstrap.pypa.io/get-pip.py | python3.6 RUN pip install pygame ADD ./start.sh /opt/start.sh ENTRYPOINT [ "bash","/opt/start.sh" ] ``` #### 创建start.sh脚本 ```bash cat start.sh ``` ```bash #!/bin/bash screen_width=${1:-800} screen_height=${2:-600} screen_depth=${3:-16} run_file=${4:-"main.py"} nohup Xvfb :1 -screen 0 ${screen_width}x${screen_height}x${screen_depth} >/dev/null 2>&1 & sleep 0.1 nohup /usr/bin/x11vnc -display :1.0 -passwd ${X11VNC_PASSWORD:-password} >/dev/null 2>&1 & sleep 0.1 DISPLAY=:1.0 export DISPLAY cd /run_game PYTHONIOENCODING=utf-8 python $run_file ``` #### 创建pygame镜像 ```bash docker build -t sheyinsong/pygame:1.0 . ```   ### Novnc安装 #### 编辑Dockerfile ```bash cat Dockerfile ``` ```bash FROM python MAINTAINER sys "sheyinsong" RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get install -y python-numpy RUN apt-get clean RUN pip install redis RUN pip install simplejson ADD ./noVNC/ /noVNC/ CMD ["python", "/noVNC/utils/websockify/run", "--web", "/noVNC", "0.0.0.0:10240", "--target-config", "/noVNC/token.list"] ``` #### 下载noVNC 下载地址:https://github.com/novnc/noVNC/releases/ #### 创建token.list(token映射到对应的socket) ```bash cat noVNC/token.list #noVNC下载好后,到根目录创建该文件 ``` ```bash a35fe7f7fe8217b4369a0af4244d1fca: 127.0.0.1:5000 03b264c595403666634ac75d828439bc: 127.0.0.1:5001 ... ``` #### 创建novnc镜像 ```bash docker build -t sheyinsong/noVNC:1.0 . ``` #### 运行novnc容器 ```bash docker run -d --network="host" --restart=always -it sheyinsong/novnc:1.0 ```   #### 创建生成NoVNC地址的脚本 ```bash cat pygame_url.py ``` ```python #coding:utf-8 import sys import os import random import subprocess import hashlib import time import argparse import json parser = argparse.ArgumentParser() parser.add_argument("pygame_dir",nargs = 1, help="Pygame progrom root directory") parser.add_argument("--screen-width", help="Xvfb screen width,default value 800", default=800, type=int) parser.add_argument("--screen-height", help="Xvfb screen heigth,default value 600", default=600, type=int) parser.add_argument("--screen-depth", help="Xvfb screen depth,default value 16", default=16, type=int) parser.add_argument("--run-file", help="run specify file", default='main.py') args = parser.parse_args() pygame_dir=args.pygame_dir[0] screen_width=str(args.screen_width) screen_height=str(args.screen_height) screen_depth=str(args.screen_depth) run_file=args.run_file pygame_image='sheyinsong/pygame:1.0' sample_url='http://192.168.50.252:10240/vnc_lite.html?path=?token=' #地址为noVNC运行的地址 dict_data={} if not os.path.isdir(pygame_dir): dict_data['result']='error' dict_data['msg']=pygame_dir+'程序目录不存在.' print(json.dumps(dict_data)) exit(1) pygame_main=os.path.join(pygame_dir,'main.py') if not os.path.exists(pygame_main): dict_data['result']='error' dict_data['msg']=pygame_dir+'程序目录不存在文件main.py.' print(json.dumps(dict_data)) exit(1) source = open(pygame_main, 'r').read() + '\n' try: code_obj=compile(source, pygame_main, 'exec') except Exception as e: dict_data['result']='error' dict_data['msg']=str(e) print(json.dumps(dict_data)) exit(1) def get_free_random_port(): command="netstat -ltnp |grep x11vnc|awk '{ print $4}'|awk -F: '{ print $NF}'" p = subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True) usage_port_list=[ int(port) for port in p.stdout.read().split('\n') if port != '' ] free_post_list=set(range(5000,6000))-set(usage_port_list) return random.choice(list(free_post_list)) def convert_png(): command="find {0} -name *.png|while read line;do; convert $line $line; done".format(pygame_dir) subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True) free_random_port=get_free_random_port() convert_png() docker_out = subprocess.Popen(['docker', 'run','-ti','--cpus','0.2', '-d', '-p', str(free_random_port)+':5900', '-v', pygame_dir+':/run_game', pygame_image, screen_width, screen_height, screen_depth, run_file], stdout=subprocess.PIPE,stderr=subprocess.STDOUT) stdout,stderr = docker_out.communicate() time.sleep(1) containter_id=stdout.strip() p1 = subprocess.Popen('docker logs --tail 100 '+containter_id,stdout=subprocess.PIPE,shell=True) p2 = subprocess.Popen('grep -A 1000 Traceback',stdin=p1.stdout,stdout=subprocess.PIPE,shell=True) docker_out=p2.stdout.read() if docker_out: dict_data['result']='error' dict_data['msg']=docker_out.replace('\r\n','\\n').replace('"','\'') print(json.dumps(dict_data)) else: token=hashlib.md5(str(free_random_port)).hexdigest() url=sample_url+token+'&password=password' dict_data['result']='ok' dict_data['msg']=docker_out.replace('\r\n','\\n').replace('"','\'') dict_data['url']=url dict_data['cid']=containter_id print(json.dumps(dict_data)) ```   #### 运行脚本 ```bash python /data/app/pygame/pygame_url.py /data/app/pygame/Puser/ --run-file main.py ``` 程序返回结果(url为访问pygame的地址) ```json {"msg": "", "url": "http://192.168.50.252:10240/vnc_lite.html?path=?token=fcd4c889d516a54d5371f00e3fdd70dc&password=password", "result": "ok", "cid": "23cac4bb78e93f3fb1c7232d5778ad99468cd86cc927cfd8e5161e485239b46b"} ``` `/data/app/pygame/Puser/` 为程序根目录 `main.py` 为运行pygame的入口程序 ### 页面访问pygame ![](https://files.ynotes.cn/pygame_demo.png)
阅读 13681 评论 1 收藏 0
阅读 13681
评论 1
收藏 0

兜兜    2019-05-24 15:36:32    2019-11-14 14:29:32   

python pygame
### 流程: - 1.用户在线编辑或者上传代码 - 2.用户点击执行按钮 - 2.1 首先把页面的代码保存到执行目录 - 2.2 ajax调用代码运行API (注:获取访问pygame的链接) - 2.2.1 API收到请求做处理 - 2.2.1.1 运行pygame(docker)生成可访问链接 - 2.2.2 API返回链接 - 2.3 ajax页面获取到API调用结果 - 2.4 把链接加载到页面Iframe展示 &emsp; ### 上传代码和保存代码 #### 上传功能代码 视图view.py(注:只提取功能相关代码) ```python @login_required def pygame(request): upload_error='' code='' code_dir='' if request.method == 'POST': myfile=request.FILES.get('myfile', None) if myfile: myfile = request.FILES['myfile'] fs = FileSystemStorage() file_path=os.path.join('pygame',str(request.user.id)) filename = fs.save(file_path, myfile) temp_dir=os.path.join(os.path.dirname(fs.path(filename)),str(request.user.id),'tempDir') #判断临时目录是否存在存在则删除目录,创建目录 if os.path.exists(temp_dir): shutil.rmtree(temp_dir) os.mkdir(temp_dir) #判断文件是否为zip if zipfile.is_zipfile(fs.path(filename)): zip_ref = zipfile.ZipFile(fs.path(filename), 'r') zip_ref.extractall(temp_dir) is_exist_main_file=0 for fname in zip_ref.namelist(): if 'main.py' in fname: main_file=os.path.join(temp_dir,fname) is_exist_main_file=1 if is_exist_main_file: code=fs.open(main_file,'r').read() code_dir=os.path.dirname(main_file) else: upload_error='压缩包根目录下不存在main.py' zip_ref.close() fs.delete(filename) else: upload_error='文件不是压缩文件' return render(request, 'blog/pygame.html',{'upload_error': upload_error,'code':code,'code_dir':code_dir}) ``` &emsp; 模板blog/pygame.html(注:只提取功能相关代码) ```html <form method="post" class="form-inline" enctype="multipart/form-data"> {% csrf_token %} <div class="form-group"> <input type="file" id="myfile" name="myfile"> </div> <div class="form-group"> <button class="btn btn-primary" type="submit" >上传</button> </div> <label for="myfile">注意:请上传zip压缩包,且压缩包根目录包含main.py</label> </form> ``` &emsp; ### 保存功能代码 #### 视图view.py(注:只提取功能相关代码) ```python def pygame_save_code(request): code_dir=request.POST.get('codeDir','') code_content=request.POST.get('codeContent','') if not code_content: HttpResponse('{"error":"代码为空"}') #判断是否有codeDir,如果有则直接写入到当前目录,如果没有写入一个用户随机目录 if not code_dir: datetime_str=datetime.now().strftime('%Y_%m_%d_%H_%M_%S') create_dir=os.path.join(django_settings.MEDIA_ROOT,'pygame',str(request.user.id),datetime_str) os.mkdir(create_dir) code_dir=create_dir #把代码写入文件,返回写入的目录 file=open(os.path.join(code_dir,'main.py'),'w+') file.write(code_content) file.close() return HttpResponse({'{"NewCodeDir":"'+code_dir+'"}'}) ``` &emsp; #### 模板blog/pygame.html(注:只提取功能相关代码) ```html //代码编辑窗口 <textarea class="form-control line_number" id="yourcode" cols="80" rows="30"></textarea> //pygame展示窗口 <iframe id="pygame_iframe" src="" ></iframe> <button class="btn btn-success" id="pygame_run" type="button">运行</button>:write //处理代码运行逻辑 $('#pygame_run').click(function() { var code_content=$("#yourcode").val(); var codedir=$("#codeDir").val(); var token = $('input[name=csrfmiddlewaretoken]').val(); //调用保存编辑pygame代码API $.post('/blog/pygame_save_code/', { codeDir: codedir, codeContent: code_content, csrfmiddlewaretoken: token}, function(returnedData){ var obj = jQuery.parseJSON(returnedData); var NewCodeDir=obj.NewCodeDir; $("#codeDir").val(NewCodeDir); setTimeout( function() { var codedir=$("#codeDir").val(); //调用执行pygame程序的API $.post('https://pygame.ynotes.cn:3443/pygame/', { codeDir: codedir}, function(returnedData){ var obj = jQuery.parseJSON(returnedData); if(obj.hasOwnProperty('url')){ $('#pygame_iframe').attr('src', obj.url); $('#pygame_text').text('运行成功'); $('#pygame_text').css('color', 'green'); } if(obj.hasOwnProperty('error')){ console.log(obj.error); $('#pygame_text').text(obj.error); $('#pygame_text').css('color', 'red'); } }).fail(function(){ console.log("error"); }); }, 100); }).fail(function(){ console.log("error"); }); }); ``` &emsp; ### 生成访问pygame链接 #### 视图view.py ```python def index(request): code_dir=request.POST.get('codeDir','nodir') command='python /data/app/pygame/pygame_url.py '+code_dir p = subprocess.Popen(command,stdout=subprocess.PIPE,shell=True) pygame_out=p.stdout.read() return HttpResponse(pygame_out) ``` &emsp; #### 生成pygame访问链接的python脚本 pygame_url.py ```python #coding:utf-8 import sys import os import random import subprocess import hashlib import time pygame_image='codemax/pygame:v3' sample_url='https://pygame.ynotes.cn:2443/vnc_lite.html?path=?token=' if len(sys.argv) < 2: print('{ "error":"请指定pygame程序目录"}') exit(1) pygame_dir=sys.argv[1] if not os.path.isdir(pygame_dir): print('{ "error":"'+pygame_dir+'程序目录不存在."}') exit(1) pygame_main=os.path.join(pygame_dir,'main.py') if not os.path.exists(pygame_main): print('{ "error":"'+pygame_dir+'程序目录不存在文件main.py."}') exit(1) source = open(pygame_main, 'r').read() + '\n' try: code_obj=compile(source, pygame_main, 'exec') except Exception as e: print('{"error":"'+str(e)+'"}') exit(1) def get_free_random_port(): command="netstat -ltnp |grep x11vnc|awk '{ print $4}'|awk -F: '{ print $NF}'" p = subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True) usage_port_list=[ int(port) for port in p.stdout.read().split('\n') if port != '' ] free_post_list=set(range(5000,6000))-set(usage_port_list) return random.choice(list(free_post_list)) free_random_port=get_free_random_port() docker_out = subprocess.Popen(['docker', 'run', '-d', '-it','--cpus=0.2','-p', str(free_random_port)+':5900', '-v', pygame_dir+':/run_game', pygame_image], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout,stderr = docker_out.communicate() time.sleep(1) containter_id=stdout.strip() p1 = subprocess.Popen('docker logs --tail 100 '+containter_id,stdout=subprocess.PIPE,shell=True) p2 = subprocess.Popen('grep -A 1000 Traceback',stdin=p1.stdout,stdout=subprocess.PIPE,shell=True) docker_out=p2.stdout.read() if docker_out: print('{"error":"'+docker_out.replace('\r\n','\\n').replace('"','\'')+'"}') else: token=hashlib.md5(str(free_random_port)).hexdigest() url=sample_url+token+'&password=password' print('{"url":"'+url+'"}') ``` &emsp; ### 构建pygame镜像(拥有运行pygame代码) #### DockerFile ```yaml FROM ubuntu:xenial LABEL maintainer "sheyinsong@unotes.co" EXPOSE 5900 6099 ENV \ DEBIAN_FRONTEND="nonintractive" \ X11VNC_PASSWORD="password" RUN apt-get update -y RUN apt-get install -y xvfb x11vnc python libglib2.0-0 ADD ./get-pip.py /opt/get-pip.py RUN python /opt/get-pip.py ADD ./pygame-1.9.6-cp27-cp27mu-manylinux1_x86_64.whl /opt/pygame-1.9.6-cp27-cp27mu-manylinux1_x86_64.whl RUN pip install /opt/pygame-1.9.6-cp27-cp27mu-manylinux1_x86_64.whl ADD ./start.sh /opt/start.sh ENTRYPOINT /opt/start.sh ``` #### 文件start.sh ```bash #!/bin/sh Xvfb :1 -screen 0 800x600x16 & sleep 0.1 /usr/bin/x11vnc -display :1.0 -passwd ${X11VNC_PASSWORD:-password} & sleep 0.1 DISPLAY=:1.0 export DISPLAY cd /run_game timeout 3600 python main.py ``` &emsp; ### 构建noVNC镜像(用于html访问pygaem(docker)中的GUI界面) #### DockerFile ```yaml FROM python MAINTAINER sys "sheyinsong@unotes.co" RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get install -y python-numpy RUN apt-get clean RUN pip install redis RUN pip install simplejson ADD ./noVNC/ /noVNC/ CMD ["python", "/noVNC/utils/websockify/run", "--web", "/noVNC", "0.0.0.0:10240", "--target-config", "/noVNC/token.list"] ``` &emsp; #### noVNC下载链接 https://github.com/novnc/noVNC/releases
阅读 1541 评论 0 收藏 0
阅读 1541
评论 0
收藏 0