最近组里有个小需求要管理数字字典,数仓的字典在HUE中能够查看,但Mysql的字典呢?于是就想对HUE进行二次开发,增加一个App使其对Mysql的数字字典进行管理,也就是一个类似METASTORE TABLE的功能。

准备开发环境

导入IDE的HUE工程不是源代码,而是已经build成功的HUE工程。

eclipse pydev插件安装

本来想用Intellij idea的,由于Intellij是在物理机(windows)上安装的,但HUE在windows下编译比较费劲,而之前HUE已在虚拟机中编译成功,虚机中又刚好有个eclipse,就选择用eclipse进行开发了。

IDE选好,就是为eclipse安装插件使其能够进行python开发,安装的插件是pydev,安装方法分为在线离线两种,在线在虚机中的eclipse总是有点问题,显示插件安装成功可是总是找不到,换了n个版本的eclipse依然不行。就只好离线安装了,在http://sourceforge.net/projects/pydev/files/pydev/下载安装包,解压之后将featuresplugins复制到eclipse的home目录下。

1
2
cp -r /pydev_home/features /eclipse_home/
cp -r /pydev_home/plugins /eclipse_home/

将HUE导入IDE eclipse

1、python插件安装好之后,就是为HUE单独设置python环境,之所以单独设置python环境,是因为HUE在build的时候已经将所依赖的python包下载至/HUE_HOME/build/env/中,为HUE设置python环境

单击Window->Preferences,找到安装好的python插件PyDev,通过右侧的new选择*/home/hadoop/hue-3.9.0/build/env/bin/python*,如图
![HUE python环境](/blogimgs/HUE App/HUE_Python.png “HUE python环境”)

2、在eclipse中新建一个python django项目并导入HUE工程
选择django项目
![新建django项目](/blogimgs/HUE App/new_project.png “新建django项目”)

下一步选择HUE相关的设置,如下图:
![导入HUE](/blogimgs/HUE App/new_project_2.png “导入HUE”)
其中1是新建django项目的名字,由于要导入已有项目,则将2勾选去掉,接着在3处选择hue的安装目录(我这编译目录和安装目录是同一个目录),别忘了在4处选择上一步中单独为hue设置的python环境。

3、设置myHue工程
设置项目属性,内容如下图(设置的两个文件都提示未找到,不知何原因,是否有影响),
![项目属性](/blogimgs/HUE App/new_project_3.png “项目属性”)

4、debug启动HUE
单击run->DEBUG Configurations,双击PyDev Django
![debug启动](/blogimgs/HUE App/new_project_4.png “debug启动”)

主模块设为buid/env/bin/hue,在有些eclipse中会报找不到该文件的提示,此时可以将hue拷贝为hue.py,刷新项目后再用Main Module右边的Browse按扭选取该hue.py,或直接输入${workspace_loc:hue/build/env/bin/hue.py}做为主模块。

启动hue时还需输入参数,在Arguements选项中输入runcherrypyserver,如图
![debug启动](/blogimgs/HUE App/new_project_5.png “debug启动”)

5、最后还得修改一处代码,在desktop/core/src/desktop/appmanager.py中的_import_module_or_none方法my_file = re.sub(r'\.pyc','.py', __file__)处添加return None,将后面的内容注释掉,完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def _import_module_or_none(module):
"""Like import_module, but returns None if the module does not exist.
This will properly handle nested ImportErrors in such a way that, if the
module should exist but throws ImportError, we *will* raise through
that error.
"""
try:
__import__(module)
return sys.modules[module]
except ImportError, ie:
# If the exception came from us importing, we want to just
# return None. We need to inspect the stack, though, so we properly
# reraise in the case that the module we're importing triggered
# an import error itself.
tb = sys.exc_info()[2]
top_frame = traceback.extract_tb(tb)[-1]
err_file = re.sub(r'\.pyc','.py', top_frame[0])
my_file = re.sub(r'\.pyc','.py', __file__)
return None
# if err_file == my_file:
# return None
# else:
# LOG.error("Failed to import '%s'" % (module,))
# raise

随后就可以在python代码中设置断点,启动debug配置进行调试。

App开发demo实例

App开发其实也比较简单,文档说的还算详细,但往往实际操作时难免会遇到一些错误,下面就来看下我在开发这个demo的过程中遇到的一些问题,我使用的版本是3.9.0,版本不一样遇到的问题可能也不一样

1、在HUE_HOME目录下cd到apps目录,之所以要cd到apps目录,因为随后执行的创建app的命令会在当前目录下新建app目录,为了将所有的app放在apps目录下,所以要先cd到apps目录。

2、在apps目录下执行新建命令

1
../build/env/bin/hue create_desktop_app mytest

新建之后,文档的下一步是执行注册app,但是经我实测,3.9.0还需进行一些修改,如下:
3、修改新建App mytest目录下的Makefile,增加APP_NAME = mytest

4、更改App mytest的static的目录结构。进入/home/hadoop/hue-3.9.0/apps/mytest/src/mytest/static目录,新建App同名的文件夹mytest,将原static目录下的文件移到新建的mytest文件夹中。如果此步没有执行的话,该App的一些静态文件就会找不到,出现404错误Http404: "/home/hadoop/hue-3.9.0/build/static/mytest/art/icon_mytest_48.32900276221d.png" 不存在

5、可以在App mytest目录下的setup.py文件中修改下该app的一些信息,比如 描述信息、作者等等。。。

6、修改代码。修改mytest中的shared_components.mako代码,将<img src="${ static(mytest + '/art/icon_mytest_48.png') }" class="app-icon" />
中的mytest加上引号,否则会报TypeError: unsupported operand type(s) for +: 'Undefined' and 'str'

7、接下来就是注册App,依然是在apps目录下执行命令

1
../build/env/bin/python ../tools/app_reg/app_reg.py --install mytest --relative-paths

可以使用命令build/env/bin/python tools/app_reg/app_reg.py --list 查看已安装app的信息。

现在就可以正常访问页面了。此时只是一个类似hello-world的功能,我们在此基础上再增加一点小功能,一个小小的计算器。

8、HUE的template语言是用mako写的,则修改mytest/src/mytest/templates/index.mako文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%!from desktop.views import commonheader, commonfooter %>
<%namespace name="shared" file="shared_components.mako" />

${commonheader("mytest", "mytest", user, "100px") | n,unicode}

## Main body

<div class="container-fluid">
% if op:
<span>${a} ${op} ${b} = ${result}</span>
% endif
<form action=${url("mytest.views.index")} method=POST>
<input name="a">
<input type="radio" name="op" value="add">+</input>
<input type="radio" name="op" value="subtract">-</input>
<input type="radio" name="op" value="multiply">*</input>
<input type="radio" name="op" value="divide">/</input>
<input name="b">
<input type="submit" value="Calculate">
</form>
</div>
${commonfooter(messages) | n,unicode}

现在页面已经写好,接下来就是views里的计算逻辑了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python

from desktop.lib.django_util import render
import operator

OPS=dict(add=operator.add, subtract=operator.sub, multiply=operator.mul, divide=operator.truediv)
OP_STRING=dict(add="+", subtract="-", multiply="*", divide="/")

def index(request):
if "op" not in request.REQUEST:
return render('index.mako', request, dict())
a = float(request.REQUEST["a"])
b = float(request.REQUEST["b"])
op = request.REQUEST["op"]
result = OPS[op](a, b)
return render('index.mako', request,
dict(a=a, b=b, op=OP_STRING[op], result=result))

此时就可以去测试下mytest的新功能了,很不幸报错了,错误信息如下:

1
2
CSRF Error (403)
Sorry, your session is invalid or has expired. Please go back, refresh the page, and try your submission again.

CSRF(Cross-site request forgery 跨站请求伪造)失败,在index.mako中添加${ csrf_token(request) | n,unicode }即可,正确代码如下:

1
2
3
4
5
6
7
8
9
10
<form action=${url("mytest.views.index")} method=POST>
${ csrf_token(request) | n,unicode }
<input name="a">
<input type="radio" name="op" value="add">+</input>
<input type="radio" name="op" value="subtract">-</input>
<input type="radio" name="op" value="multiply">*</input>
<input type="radio" name="op" value="divide">/</input>
<input name="b">
<input type="submit" value="Calculate">
</form>

最后看下页面吧,
![app_demo](/blogimgs/HUE App/app_demo_done.png “app_demo”)

Done。。。。

Mysql元数据管理App开发

Mysql元数据管理功能是一个类似hive元数据管理的管理模块,可以查看mysql数据库中的databases和tables以及修改他们的COMMENT信息。具体实现是拷贝hive元数据管理的代码,在此基础上进行了修改,完成此功能的,目前只支持mysql,且要使用此功能时需要配置DB query功能。

此功能是在hue3.7版本上开发的。

下面就看几个页面吧,代码随后整理下会把链接放出来。
![msyql_01](/blogimgs/HUE App/mysql_01.png “mysql_01”)
![mysql_02](/blogimgs/HUE App/mysql_02.png “mysql_02”)

开发起来难度不是很大,不能不说大神写的代码就是强,有些代码虽然看不懂,但一改就实现了想要的功能,接口性特别强。

参考

http://wanshi.iteye.com/blog/2173415
http://cloudera.github.io/hue/docs-3.7.0/sdk/sdk.html#fast-guide-to-creating-a-new-hue-application