TA的每日心情 | 擦汗 4 天前 |
---|
签到天数: 1042 天 连续签到: 4 天 [LV.10]测试总司令
|
背景
接触python两周有余了。越来越喜欢这门语言,因为做什么都足够快,不像java那么笨重。虽然它还是那么容易出错。但是构建一个并不复杂的小工具的时候,它显的效率的多。就像海盗一样:快,狠,准。这里我就不比较java和python的优劣了,这个话题太大了,以我的水平也怕评论不好。我们就各自使用自己认为最适合项目的语言就好了。刚学python的第一周先是学习了一下基本语法,环境,IDE(好在有几年的java底子,学的比较快),然后用pytest和allure做了测试的基础架子,准备测试我们提供的python版本的SDK。之后用python重写了一遍环境管理的那一堆shell脚本,比shell少些了很多代码,之后扩展也好,维护也好都方便了很多(后悔这么晚才学python中~~~)。上周末在家开始学习django和flask, 考虑到底选什么作为建站的框架。后来还是选了flask,因为够小,够灵活。我暂时的需要可能不需要数据库之类的东西。所以也就没选什么都包办了的django。于是我拿我们环境管理的需求练手。之前是通过一堆脚本控制的,现在我做一个web,可以通过在页面上点点点就能管理好这些环境。我想一边练习一边记录其中的步骤,给像我一样刚接触的人一个快速上手的指引吧。由于都是业余时间做,所以更新的会比较慢。
Flask
这里介绍一下Flask给像我这种刚刚接触的人把。它给我的感觉就是个十分轻量级的,轻量级到令人发指的web框架。用过Flask之后才知道springMVC到底有多重。它使用起来也很简单,不需要任何web容器。 直接按下面的方式写一小段代码就可以了
- from flask import Flask
- app = Flask(__name__)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run(host='0.0.0.0', debug=True, port=9537)
复制代码 当你运行这个脚本后,就可以在浏览器访问了。是不是够简单。同样它也支持像django那么大而全的功能,不过这些功能并没有被集成到Flask里面。 而是在你需要的时候通过安装相应的模块来实现。例如支持数据库的orm框架:Flask-SQLAlchemy 支持数据库迁移,升级等功能的模块:Flask_Migrate。 支持页面表单管理的模块:Flask-WTF。 这些都是需要的时候再安装就可以了。
学习资料
贴一下我找到的学习资料
Flask官方文档的中文翻译版本:http://docs.jinkan.org/docs/flask/index.html
Flask所有扩展模块的文档:http://flask.pocoo.org/extensions/
一个我觉得比较好的博客:http://www.pythondoc.com/flask-mega-tutorial/database.html#id2
开始
OK,我们首先安装Flask吧,直接用pip安装就好了。暂时我们的功能还很简单,不需要其他的扩展模块。先贴一个图片看看我们的项目结构。
我从上到下开始介绍吧
env_info是我们所有环境的配置文件,暂时我没有用数据库来保存这些信息。里面都是ini文件,详情不表了。
lib 是我放置控制docker的脚本的地方。如大家所见,都是些compile,deploy什么的
web 是我们Flask的根目录
static是我们放置静态文件的地方,例如css,js,图片什么的。注:这是Flask的默认设置
templates是我们所有模板的文件,大家可以先理解为所有的html文件都放在这里。注:这是Flask的默认设置
首先我们先在web目录的init.py文件里写一下我们Flask的配置:
- # coding=utf-8
- from flask import Flask
- app = Flask(__name__)
- # 如果想要使用Flask-WTF的表单,需要一个config文件
- app.config.from_object('config')
复制代码 接着创建一个urls.py文件,用来编写我们的路由和启动命令。就如我们上面的例子一样,我们这样写
- if __name__ == '__main__':
- app.run(host='0.0.0.0', debug=True, port=9537)
复制代码 之后运行urls.py就算是我们的启动脚本了。然后写一个首页的路由方法。如下:
- @app.route('/')
- @app.route('/index', methods=['GET', 'POST'])
- def index():
- configs = list_all_config()
- return render_template('index.html', configs=configs)
复制代码 我们来解释一下吧。 @app.route, 就是我们的路由方法,可以配置多个, 也就是多个url都可以绑定到这一个方法上。 methods参数设定了方法类型。 可以看到方法里面我们取了所有的配置信息(就是env_info中所有的配置信息)。我们希望将所有的配置信息都展示在首页上。 所以取出信息以后,我们使用render_template这个渲染模板的方法,将index.html进行渲染并把configs传递到模板中(在Flask中页面就是模板)。 好了,现在我们去看看这个index.html里是怎么写的吧。
- <div class="row clearfix">
- {% for config in configs %}
- <div class="col-md-4 column">
- <h2>
- {{ config.name_prefix }}
- </h2>
- <p>
- </p>
- <p>
- <a class="btn" href="#">查看详情 »</a>
- <a class="btn" href="/restart/{{ config.name_prefix }}">部署环境 »</a>
- </p>
- </div>
- {% endfor %}
- </div>
复制代码 这里来看一下Flask内置的Jinjia2的语法吧。流程控制语句使用{% content %} 来表示, 直接把元素输出到页面上使用{{% element %}}。 上面的代码就是说,我循环一下传递进来的configs列表。把每个配置的名字都展示到页面上,然后每个配置都有两个链接,分别是查看配置的详情和部署环境。这个是后话了。 现在如果我们启动web服务,使用浏览器访问就会看到所有的配置都显示在了我们的主页上。 但是还有点不够,那就是我们的页面实在太难看了。 没有任何的布局。 但是我还是个前端的小白,让我重头学一遍css,js什么的太费时间了。 所以我们可以取巧一下,偷懒一下。来给我们的web换个装
bootstrap
换装工具就是bootstrap了。 下载地址如下: http://getbootstrap.com/
我们要把下载下来的文件解压,然后放到static目录下,如图:
然后我们在index.html加入引用
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <!-- 引入 Bootstrap -->
- <link href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
- <!-- HTML5 Shim 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 -->
- <!-- 注意: 如果通过 file:// 引入 Respond.js 文件,则该文件无法起效果 -->
- <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
- <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
复制代码 接着我们使用在线的页面生成工具:http://www.runoob.com/try/bootstrap/layoutit/#
我们用这个工具拖拽好自己的布局之后。就可以点击那个下载。把HTML代码弄到我们的页面里。 然后我们就有了如下的页面了
好吧,我不具备什么审美天赋。也没下什么大工夫。看着顺眼就行。
最关键的功能
好了,到了最关键的功能了,那就是部署一个环境。 所以在我们的首页中才会有这样一个链接:
- <a class="btn" href="/restart/{{ config.name_prefix }}">重新启动 »</a>
复制代码 现在我们需要为这个链接创造另一个路由方法。如下:
- @app.route('/restart/<name>', methods=['GET', 'POST'])
- def restart(name):
- workspace = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
- path = workspace + '/boot.sh'
- config = workspace + '/env_info/' + name + '.ini'
- command = 'bash %s %s' % (path, config)
- result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- jobs = Jobs.jobs
- if name in jobs.keys():
- jobs[name].kill()
- jobs.pop(name)
- jobs[name] = result
- return render_template('restart.html', name=name)
复制代码 这里我们看到了一个用法/restart/。 这个<>中的name就是页面向我们传递的参数。 所以在restart方法里我们才可以使用name这个参数。我们根据name找到相应的配置文件并使用subprocess模块进行调用。由于我们希望这个步骤是异步的,不能再部署过程中页面一直处于卡死的状态。所以使用Popen这个函数来调用。 我们把标准输出和标准错误都重定向到管道中。把这个调用后返回的对象保存在一个全局的dict中。方便之后取log的时候用。 那么现在我们可以在页面上点击链接进行部署了。但是有一个问题,就是我们不知道部署当前是失败了还是成功了。 我们希望能实时的在页面上看到部署的日志, 我们需求页面能实时的显示最新的log而不必刷新页面。
Jquery Ajax
我这个前端水平就只能想到这样一个方案了,现在我们去现在一个Jquery.js吧。 同样放到static目录下。
然后我们再搞一个log页面, 在head标签中我们引入Jquery。
- <script type=text/javascript src="{{ url_for('static', filename='jquery.js') }}"></script>
复制代码 然后我们加入下面的一段JS:
- <script type=text/javascript>
- $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
- </script>
- <script type=text/javascript>
- $(function () {
- for (var i = 1; i < 10000; i++) {
- $.get($SCRIPT_ROOT + '/log/{{ name }}', function (data) {
- $("#log").append(data);
- })
- }
- });
- </script>
- <dev id="log"></dev>
复制代码 我来解释一下上面这段JS。 $.get方法就是动态执行一个请求的方法,后面跟上url。 $("#log")是JS的选择器,用来定位页面元素,意思是找到id为log的元素并执行append的操作。所有的代码的意思就是循环不停的调用这个get方法,并将返回值作为data传递给之后的function,这个function用来定位元素并把日志追加到页面上。 那么现在,我们唯一缺的就是再写这么一个路由方法实时读取日志了。如下:
- @app.route('/log/<name>')
- def update_log(name):
- # time.sleep(1)
- job = Jobs.jobs[name]
- # error = job.stderr.readline()
- out = job.stdout.readline()
- return '<p>'+out+'</p>'
复制代码 看过之前restart方法的代码,我们知道所有的Popen返回的结果对象都放到了一个全局的dict里。 现在我们根据页面AJax传递name把它找出来。然后读取一行日志信息返回。OK,万事大吉。 我不会截动态图~~ 大家见谅。。。
唠叨两句
我知道前端的发展现在已经很厉害了,Jquery这种玩意都不知道过时了多久,我们公司的前端都已经在用React.js。可我是没那么多时间去学了。反正我们做的都是给项目内部用的,所以我想不用纠结什么前沿技术了,够用就好。目前我就做了这么多。 大概实现了最主要的几个功能:显示配置,部署环境,实时获取log。之后加上创建配置和修改配置的功能后就基本上达到可用状态了。额,想想在老东家的时候用springMVC为了达到可用状态可是比这多花了不少的时间。所以我现在很喜欢python,在构建这些工具的时候效率更高,更快。 有个小段子是我最开始请教我司写SDK的小伙子的时候,问他你为什么选择用python,他回答说:为了多活两年。 恩,现在我有点理解他为什么这么说了。 之后我会慢慢的完善这个小网站,就当是练手了,然后我会慢慢的把过程都记录下来发到社区里,一个是对我的学习和工作有个记录,再一个也希望对一些同学有所帮助。毕竟在工作中做测试平台,测试工具什么的可能都要封装成一个web。由于这纯是我的个人意愿,组织上并没有给我在工作中安排时间去做。 所以我都是抽空搞一搞,进度可能有点慢。希望之后能成熟到给一些非技术人员,例如产品,售前的同学去搭建一个他们需要的环境,而不再需要懂技术的人跑去给他们运行脚本部署。恩~ 保持学习
发表于TesterHome
|
|