Nginx+FastCGI+MySQL+Linux搭建Trac 0.12.2环境

完整详细的安装方法请参考官网http://trac.edgewall.org/wiki/TracInstall或者INSTALL
本文主要记录我在安装的时候遇到的一些问题和解决办法,有误导的地方欢迎指正

一般都是装数据库,装服务器,装Trac程序

0、安装Python环境,默认都有,只需要检查版本是否符合,安装需要的Python插件

1、安装数据库,我这里用的是MySQLhttp://lucane.iteye.com/blog/866355
Trac是Python写的,访问MySQL用的是MySQL-python
需要配置的就是$MYSQL_PYTHON_HOME/site.cfg/site.cfg
下的mysql_config = $MYSQL_HOME/bin/mysql_config
目前我的这些东西都在一台机器上,如果分布在多台机器上还真不知道怎么装

2、安装Nginx,我这里是编译安装
./configure –prefix=$PATH_TO_INSTALL/nginx
make
make install
可能启动的时候报错,类似于下面这样
[emerg]: bind() to 0.0.0.0:80 failed (13: Permission denied)
这是因为端口号的问题,在Linux中1024下的端口号都需要root用户才能使用
所以普通用户启动程序绑定会报出权限问题,我这里是把Nginx绑定的端口号改为8888

Nginx比较简陋,启动之后关闭一般要通过kill命令杀进程,具体的参考Nginx相关手册
这里列出几个简单的

修改配置文件后检查配置文件语法是否正确
nginx -t -c $NGINX_HOME/conf/nginx.conf
t因该是表示测试的意思,c就是指定所用配置文件的地方,这里可以自由发挥

停止Nginx
kill -quit PID_OF_NGINX

平滑重启Nginx
kill -hup PID_OF_NGINX

启动Nginx就不说了

关键是这个配置文件,这里比较复杂,至少对于新手来说是这样的
我也是参考了http://marshalys.blogspot.com/2009/02/freebsdtrac.html才弄出来的

我先把我的配置文件贴出来,可能有些无用,但是暂时就这样了
nginx.conf

    server {
        listen       8888;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            fastcgi_pass 0.0.0.0:8080;

            auth_basic "Trac Realm"; 
            auth_basic_user_file $KEY_PATH/trac.htpasswd;

            fastcgi_param PATH_INFO $fastcgi_script_name;
            fastcgi_param REQUEST_METHOD $request_method;
            fastcgi_param QUERY_STRING $query_string;
            fastcgi_param CONTENT_TYPE $content_type;
            fastcgi_param CONTENT_LENGTH $content_length;
            fastcgi_pass_header Authorization;
            fastcgi_param REMOTE_ADDR           $remote_addr;
            fastcgi_param SERVER_PROTOCOL       $server_protocol;
            fastcgi_param SERVER_PORT           $server_port;
            fastcgi_param SERVER_NAME           $server_name;
            fastcgi_intercept_errors off;

            # for authentication to work
            fastcgi_param AUTH_USER $remote_user;
            fastcgi_param REMOTE_USER $remote_user;
        }

其中
auth_basic “Trac Realm”;
auth_basic_user_file $KEY_PATH/trac.htpasswd;

# for authentication to work
fastcgi_param AUTH_USER $remote_user;
fastcgi_param REMOTE_USER $remote_user;
是Trac授权用的,可以先不用管

当然弄好之后先要测试Nginx能否将Python转发给FastCGI处理,写个简单的测试fcgi.py放到$NGINX_HOME/html/下
其中fcgi.py的内容如下

执行python fcgi.py &
打开浏览器,键入http://localhost:8888/如果能看到Hello World字样就说明OK
当然这些端口号神马的都是根据自己的实际情况

3、安装Trac
你可以把它认为是Python的模块插件
所以安装方式和安普通模块是一样的
我这里编译安装
python setup.py build
sudo python setup.py install

接下来就是创建实例了,参考INSTALL
trac-admin /path/to/projectenv initenv

需要填些数据库连接,实例名字之类的,我这里数据库连接如下
mysql://trac:trac@127.0.0.1:3306/trac
是这种格式
DATABASE_TYPE://USERNAME:PASSWD@HOST:PORT/DATABASE_INSTANCE
在HOST那里你如果填的是localhost就可能会出下面这个错误
OperationalError: (2002, “Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’ (2)”)
将localhost换成127.0.0.1就不会报错了,原因是它指定的sock和你机器上实际的sock路径不一致,填IP就强制要求通过TCP/IP之类的去连,不用socket

安装完成之后可以用它自带的服务器测试下
执行tracd –port 8000 /path/to/projectenv
打开浏览器,键入http://localhost:8000/如果能看到Trac的东西就说明OK

4、整合,把我们的Trac部署到Nginx上去
很简单,写个trac-standalone-fcgi.py放到$TRAC_HOME/cgi-bin当中,这里的$TRAC_HOME是Trac本身安装的位置,不是trac-admin创建出来的实例(或工程,项目)的位置
简单的说如果你把Trac认为是Python的一个插件,那么这个$TRAC_HOME就是这个插件的位置
trac-standalone-fcgi.py的内容如下

执行python trac-standalone-fcgi.py &
当然你需要把之前打开的一些东西都停止,比如fcgi.py或tracd等
打开浏览器,键入http://localhost:8888/如果能看到Trac的东西就说明OK
注意这些端口号和地址,要理解清楚,弄清楚

5、权限配置
如果第四步成功搞定的话,你就可以浏览些内容了,但不会发现你无法添加内容等等
这里权限用户管理有好多种方法,具体参考官网说明,我这里是把用户信息放在key文件中的,采用basic认证的方式
就是那种user:passwd键值对形式存放的
这样做的话,先要生成这样的配置文件,可以用Apache的htpasswd工具来生成
我这里使用的$TRAC_HOME/contrib/htpasswd.py来生成的
./htpasswd.py -c -b $KEY_PATH/trac.htpasswd admin admin
./htpasswd.py -b $KEY_PATH/trac.htpasswd use1 use2
注意第二个就不用-c参数了,估计这里是第一个已经创建文件了,后面的就是往里面添加内容,所以不需要-c参数
有了这个用户密码文件,还要把它和我们的Nginx关联起来,在$NGINX_HOME/conf/nginx.conf中加这两行
auth_basic “Trac Realm”;
auth_basic_user_file $KEY_PATH/trac.htpasswd;
还需要和我们的实例(或工程,项目)关联起来,一般这里只需要增加个管理员就行了(方便在Web界面中操作),命令如下
trac-admin /path/to/projectenv permission add admin TRAC_ADMIN
你也可以help看下该命令更多的用法,比如
trac-admin /path/to/projectenv permission list

这样你再重启Nginx和trac-standalone-fcgi.py就可以看见需要你输入用户名密码的提示了,这是采用HTTP的认证方式,所以会哐哐的提示你输入用户名密码
你可以安装些插件来完成普通的基于表单的验证方式

不过你密码文件创建完成之后可以通过自带的服务器验证下是否成功
tracd -p 8000 –basic-auth=projectenv,$KEY_PATH/trac.htpasswd,” /path/to/projectenv
它提示你需要输入用户名密码,用管理员登录之后能看见Admin Tab,能创建Ticket就说明OK

我在部署在Nginx上的时候,开始死命的都看不见Admin Tab,不能创建Ticket,后来发现很多人也有类似的问题
后来在$NGINX_HOME/conf/nginx.conf加入了
# for authentication to work
fastcgi_param AUTH_USER $remote_user;
fastcgi_param REMOTE_USER $remote_user;
就OK了

参考http://trac.edgewall.org/wiki/TracFastCgi

如何使用SQLiteOpenHelper

都知道在Android中SQLiteOpenHelper是用来创建和升级数据库,参考
$ANDROID_SDK_HOME/docs/reference/android/database/sqlite/SQLiteOpenHelper.html

软件发布出去了,用户已经安装使用了,但是随着软件的升级,数据库结构做了些改动,我们不希望用户把应用卸载了再装(这样会丢失应用所有的数据),我们希望在数据库总体结构和已有数据不变的情况下做些小的改动,比如新增一个字段或索引,新增加一个表等等,那么这个时候我们就要用到这个类了

常用的也就是onCreate和onUpgrade这两个方法,在使用的时候这两个方法都需要重写,里面实现自己的逻辑

我们先列出一个场景:
假设第一版程序发布出去,First Public Version,代码如下

	@Override
	public void onCreate(SQLiteDatabase db) {
		bootstrapDB(db);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	}

onUpgrade里面没有代码,第一版出去没有需要更新的,bootstrapDB方法就是些DDL和数据初始化操作等等

之后过了一段时间,新的程序发布(其中数据库结构做了些变化),这个时候已经开始使用第一版程序的用户就需要升级,我们不希望他已经存在的数据被破坏,那么我们发布出去的新的版本中代码该怎么写呢?
直接看代码,这些代码都是从Android自带的应用中抽取出来的,做了些具体业务上的简化,主要是阐述清楚用法

    @Override
	public void onCreate(SQLiteDatabase db) {
		bootstrapDB(db); // 这个方法里面都是最新版的初始化方法
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.i(TAG, "Upgrading DB from version " + oldVersion + " to "
				+ newVersion);
		if (oldVersion == 1) {
			upgradeToVersion2(db);
			oldVersion += 1;
		}
		Log.v("do upgrade", "我更新了。。。");
	}

这样如果后来又有新的程序发布,那么这两个方法会变成类似这个样子

    @Override
	public void onCreate(SQLiteDatabase db) {
		bootstrapDB(db); // 这个方法里面始终都是最新版的初始化方法
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.i(TAG, "Upgrading DB from version " + oldVersion + " to "
				+ newVersion);
		if (oldVersion == 1) {
			upgradeToVersion2(db);
			oldVersion += 1;
		}
		if (oldVersion == 2) {
			upgradeToVersion3(db);
			oldVersion += 1;
		}
		// 这是一种逐级更新的方式
                // 对于目前使用的还是第一版的用户而言,会先执行完upgradeToVersion2再执行upgradeToVersion3
                // 对于目前使用的还是第二版的用户而言,会执行upgradeToVersion3
		Log.v("do upgrade", "我更新了。。。");
	}

这样也许就能看的很清楚这个类的意图和用法了,后面版本一直增加的话,我们就一直这样写就好,保证全新的用户和升级的用户都能正常使用,那么我们如何来调用呢
一般我们会有个构造方法,有个参数就是数据库的版本,比如下面这两个构造方法

	public MyDatabaseHelper(Context context, String name, CursorFactory factory,
			int version) {
		super(context, name, factory, version);
	}

	public MyDatabaseHelper(Context context, int version) {
		super(context, NAME, null, version);
	}

采用如下方式调用

	helper = new MyDatabaseHelper(context, 10); // 这个数据库版本号会随着程序的每次发布而变化,是表示每次需要更新到的版本号,也就是最新的版本号
	sqlite = helper.getWritableDatabase();

其实更好理解这个用法就是读SQLiteOpenHelper.getWritableDatabase这个方法,里面有段代码

    int version = db.getVersion();
    if (version != mNewVersion) {
        db.beginTransaction();
        try {
            if (version == 0) {
                onCreate(db);
            } else {
                onUpgrade(db, version, mNewVersion);
            }
            db.setVersion(mNewVersion);
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

另外,看看set/get Version就知道数据库版本标记是通过PRAGMA user_version;这个命令来完成的,你也可以用sqlite3之类的工具把数据库文件打开,然后执行PRAGMA user_version查看或者设置版本值

如下是完整的两个代码,是目前在使用的

软件开发中最让人伤脑筋的几种人

本文不针对具体的人,仅仅表述现象,正确与否请自行判断

1、为什么呢,为什么呢,没有道理的啊,明明在Firefox中可以正确运行的,到了IE下面就错了呢
可能有时候会碰到人问这种或类似这种问题,他还会很兴奋的叫你过去给他看
不过一般问这种问题的人基本都还是新手,他们还不了解他们自己的工作职责是什么,老板请他来是做什么的
所以如果自己还经常问别人这种问题,请好好思考下。
如果你发现的是一个很少遇见的Bug或者你对某种通常出现的Bug提出了一种很好的解决方式,你可以和其他人交流下
记住,各种浏览器之间的差异是存在的,不要想当然

2、这个问题没有考虑到,这样做不下去了,再换种方法去看看
有可能你已经是一个“设计师”,你的工作是定下一种可行的方案,然后分给手下的人去实现它,可能你还要负责关键代码的实现
如果让你自己来设计并实现一个不是很复杂的系统,你会比较快,因为所有的东西都在你的控制之中,发现问题的时候去解决问题,这样一步一步就解决了
但现在你有更大的责任是负责整个系统及手下的人员,你要让他们去实现一部分功能,首先你给他们讲解,告诉他们怎么去做,然后他们就去按着你给的方法去做了
但过了一段时间,有人过来跟你说,按这样的实现走不下去了,你听他给的理由,然后分析下也确实是这样(不是因为某个技术难点过不去,而是整个方式不对)。
然后你就告诉他让他换种方式去实现,当然他也会照着你的意思去做,当然这极有可能会浪费很多时间。
你的岗位是什么,你的职责是什么,决定要谨慎

3、开什么调试,直接看代码
有些人总是很牛,他们不用调试,不用测试,写好的代码只要编译通过,经过他们的大脑检测“通过”,他们就敢把这些更新提交到线上
如果是这类人员,没什么好说的,建议看看《Facebook是如何管理代码的》,原文《How Facebook Ships Code
可能我们不需要很严格的审查,但是基本的测试还是必要的
你这样做只会浪费更多人力物力,当然有可能是紧急Bug的修改,偶尔可以直接在线上改,然后测
做这行就要专业点,前人的经验还是有用的,测试是必须的

4、这些错误又没有什么关系咯,放在里面好了,不用管
上级领导给你分配了一个任务,让你改改正在运行项目的一个小Bug,你把整个项目Checkout下来,发现项目里面很多错误,虽然和目前系统无关
但是影响了你通过集成开发环境部署测试,你只能改一个Bug提交一点文件到服务器上,根本无法在线下完成一个完整的测试
然后你向领导反映这个问题,项目代码中有很多错误,有很多注释掉的代码,当然最终得到的结果是没有什么改善
保证项目源码的完整性和正确性,无关的代码都清理干净,真正觉得有用目前又用不到的就用公司wiki或bbs之类的记录下来,努力写有用的文档
有时候什么狗血的需求和设计文档都是瞎扯淡的,是给别人看的
代码是写给人看的,很难看懂的代码是利用价值最低维护费用最高的东西

5、这样做也行,那样也可以吧
成员开会讨论问题中,大家提出了很多种解决问题的方法,都具有可行性,都具有优点和缺点,一个小时过去了还是没有决定采用哪种方式,最后会议在没有结果中结束
会议在一句“这样做也行,那样也可以吧”话中结束就是最大的失败,身为核心人物的你总有过人的判断力和经验吧,结合实际情况做出正确的判断是很重要的
建议可以很多,决定应该就那么一个

6、哦,这个问题我改过了,现在不是这样的了
协同开发,你要调用某个功能接口,但这个接口是由其他的人员开发的
某天你发现这结果不对了,然后你开始测试自己的程序,一轮测下来,发现调用的接口有些规则已经变化了,当然之前你都不知道
然后你去询问相关人员,结果得到了这句话
协同工作一定要清楚自己的行为会不会影响到别人,不要以自我为中心

想一想这些人当中有没有自己,我们通常是眼高手低,想当然,飘飘然。

使用Bugzilla的Whining功能

改了多少bug了,还有多少木有改,某类型的bug还有多少?

如果想周期性的知道这类问题,那么使用Whining功能吧

简单的说就是执行一个查询,然后把结果邮件通知给用户或组,这些被通知的用户可以是谁取决于设定该功能的用户的权限大小

但是目前在使用的时候遇到这么个问题,Apache日志记录如下:
editwhines.cgi: Use of uninitialized value in numeric eq (==) at $APACHE_HOME/htdocs/bugzilla/editwhines.cgi line 188
看了下代码,line 188写着“if ($cgi->param(“mailto_type_$sid”) == MAILTO_USER)”,估计是统计邮件发送的地址为空了,不大清楚mailto_type_的意思和perl的写法

以我的理解,普通用户Whining的对象就是自己,所以没有输入邮件发送对象(User或Group)的地方;超级用户可以将该邮件发送给系统的指定User或Group,所以Mail to是可以编辑的
bugzilla-whining

如果发送的统计邮件有乱码,可以试着修改邮件模板,位置$BUGZILLA_HOME/template/en/default/whine/mail.html.tmpl
在html的head之间加上

<meta http-equiv="content-type" content="text/html; charset=utf-8" />

来指定其编码,这样一般就不会乱了。

目前只成功发送过一次邮件,后台没有发现错误,求解中ING

UPDATE 2011-04-08
那次偶然的成功发送是因为我单独执行了./whine.pl的结果,也就是说在我不人为执行的情况下系统并没有自动执行

因为Bugzilla的Whine是利用crontab程序来完成定时执行任务的,于是用crontab -l查询发现没有正在执行的任务

原来我这台机器上面crond服务根本没有,所以无法执行调度

另外附上Bugzilla比较有用的可调参数
allow_attachment_display

usemenuforusers

P.S. 关于crontab,点击我还有我

在Bugzilla中通过SMTP发通知邮件

Bugzilla 4.0注册验证通知等邮件可以通过SendMail之类的来发送,配置也很简单,但是它发出去的邮件后缀地址是localhost.localdomain之类的地址,会被一些邮箱拦截,从而收不到邮件。所以通过SMTP来发邮件还是很必要的。
文档上说的配置SMTP也很简单,但是我在配置的时候遇到几个问题,在此记录下来
1、The new value for smtp_username is invalid: SMTP Authentication is not available. Run checksetup.pl for more details.
这是我在Bugzilla中Create New Account的时候发验证邮件时产生的,这个的原因是Perl有个模块没有安装,安装下就可以了
sudo perl install-module.pl Authen::SASL
自己先可以通过$ ./checksetup.pl –check-modules查看下已经安装了哪些模块
只要安装好,上述的这个问题就解决了

所以再次试着注册一个用户,提示邮件发送成功了,于是下面的问题产生了

2、提示邮件成功发送,但是实际邮件没有发送出去,因为我填的邮箱没有收到邮件
在这里我原先是开启了use_mailer_queue这个功能
于是我选择使用Test模式(Parameters -> Email -> mail_delivery_method -> SMTP)再发了一次,mailer.testfile木有邮件

网络上有人和我是一样的问题,发不出去邮件,我参看这里http://hi.baidu.com/ever__love/blog/item/62473a9772800846d0135e2c.html的方法,把use_mailer_queue关闭(Parameters -> Email -> use_mailer_queue -> Off),再试着发送一次,果真mailer.testfile当中有邮件了

这样就基本定位到问题use_mailer_queue没有正常工作

于是又把use_mailer_queue开启
执行下面的命令
$ $BUGZILLA_HOME/jobqueue.pl check
Configuration looks okay
jobqueue.pl running – pid 5317
9 jobs in the queue.

看起来似乎正常,但是提示9 jobs in the queue,并且这个数目一直没有减少,这说明邮件几乎都被堵塞在这里了,而并没有发出去

于是又Debug发现
$ $BUGZILLA_HOME/jobqueue.pl -f -d restart
Killing 9657
Starting up…
TheSchwartz::work_once found no jobs
TheSchwartz::work_once found no jobs

几乎神了,这里又说没有错误,那到底是哪里出问题了呢?
目前无解,求解ING。。。

UPDATE 后来经过分析解决了此问题
虽然debug jobqueue出来的信息开头几个没有发现什么异常,但是经过很多个“TheSchwartz::work_once found no jobs”之后出来一些错误信息,如下:
……
TheSchwartz::work_once found no jobs
TheSchwartz::work_once found no jobs
TheSchwartz::work_once found no jobs
…….
TheSchwartz::work_once found no jobs
TheSchwartz::work_once found no jobs
TheSchwartz::work_once found no jobs
TheSchwartz::work_once got job of class ‘Bugzilla::Job::Mailer’
Working on Bugzilla::Job::Mailer …
Use of uninitialized value $hostname in concatenation (.) or string at Bugzilla/Mailer.pm line 153.
job failed. considering retry. is max_retries of 725 >= failures of 5?
job failed: There was an error sending mail from ‘bugzilla_admin@’ to
‘xxxxx@aol.com’:Can’t call method “address” on an undefined value at
lib/Email/Send/SMTP.pm line 25.
TheSchwartz::work_once found no jobs
……
TheSchwartz::work_once found no jobs
TheSchwartz::work_once got job of class ‘Bugzilla::Job::Mailer’
Working on Bugzilla::Job::Mailer …
Use of uninitialized value $hostname in concatenation (.) or string at Bugzilla/Mailer.pm line 153.
job failed. considering retry. is max_retries of 725 >= failures of 5?
job failed: There was an error sending mail from ‘bugzilla_admin@’ to
‘xxxxxx@gmail.com’:Can’t call method “address” on an undefined value
at lib/Email/Send/SMTP.pm line 25.
TheSchwartz::work_once found no jobs
……
TheSchwartz::work_once found no jobs

……是我省略掉了很多这样相同的状态

看起来这个jobqueue当中的任务不是立即就执行了的,它可能是经过某种算法才执行的
否则不应该出现第一个就是“TheSchwartz::work_once found no jobs”,到后面才有任务执行的

并且进一步发现这些任务队列都是放在数据库当中的一张表(ts_job)中的,任务成功一个就把它从这张表中删除

不过此时问题还没有解决,认真看下错误,并且看了指出的错误地方的源代码,觉得应该是mailfrom填错了,可能我这里填的不完整

又打开SMTP的调试开关(Parameters -> Email -> smtp_debug -> On),这样能看到更多的详细信息,基本觉得问题就在这了

于是我填写了标准的邮件格式“username@example.com”之后再试,邮件还不没有发出去,jobqueue的的数量又增加了一个,
看来可能前面的这些包含错误信息的队列已经将后面的阻塞了,于是我试着将ts_job表当中的数据删除
truncate table ts_job; // 反正我这里是直接truncate掉了的,我不知道这里delete和truncate有没有区别

再次注册了一遍,看到后台输出通知邮件发送成功的信息,如下:
TheSchwartz::work_once found no jobs
TheSchwartz::work_once got job of class ‘Bugzilla::Job::Mailer’
Working on Bugzilla::Job::Mailer …
Net::SMTP>>> Net::SMTP(2.31)
Net::SMTP>>> Net::Cmd(2.29)
Net::SMTP>>> Exporter(5.63)
Net::SMTP>>> IO::Socket::INET(1.31)
Net::SMTP>>> IO::Socket(1.31)
Net::SMTP>>> IO::Handle(1.28)
Net::SMTP=GLOB(0xb79ce68)<<< 220 esmtp4.qq.com Esmtp QQ Mail Server Net::SMTP=GLOB(0xb79ce68)>>> EHLO localhost.localdomain
Net::SMTP=GLOB(0xb79ce68)<<< 250-esmtp4.qq.com Net::SMTP=GLOB(0xb79ce68)<<< 250-PIPELINING Net::SMTP=GLOB(0xb79ce68)<<< 250-SIZE 52428800 Net::SMTP=GLOB(0xb79ce68)<<< 250-AUTH LOGIN PLAIN Net::SMTP=GLOB(0xb79ce68)<<< 250-AUTH=LOGIN Net::SMTP=GLOB(0xb79ce68)<<< 250 8BITMIME Net::SMTP=GLOB(0xb79ce68)>>> AUTH LOGIN
……
Net::SMTP=GLOB(0xb79ce68)<<< 235 Authentication successful Net::SMTP=GLOB(0xb79ce68)>>> MAIL FROM:
Net::SMTP=GLOB(0xb79ce68)<<< 250 Ok Net::SMTP=GLOB(0xb79ce68)>>> RCPT TO:
Net::SMTP=GLOB(0xb79ce68)<<< 250 Ok Net::SMTP=GLOB(0xb79ce68)>>> DATA
Net::SMTP=GLOB(0xb79ce68)<<< 354 End data with .
Net::SMTP=GLOB(0xb79ce68)>>> From: xxxxxx@qq.com
Net::SMTP=GLOB(0xb79ce68)>>> To: xxxxxx@gmail.com
Net::SMTP=GLOB(0xb79ce68)>>> Subject: Bugzilla: confirm account creation
Net::SMTP=GLOB(0xb79ce68)>>> X-Bugzilla-Type: admin
Net::SMTP=GLOB(0xb79ce68)>>> X-Bugzilla-URL:
Net::SMTP=GLOB(0xb79ce68)>>> Auto-Submitted: auto-generated
Net::SMTP=GLOB(0xb79ce68)>>> Content-Type: text/plain; charset=”UTF-8″
Net::SMTP=GLOB(0xb79ce68)>>> MIME-Version: 1.0
Net::SMTP=GLOB(0xb79ce68)>>> Date: Tue, 05 Apr 2011 16:05:23 +0800
Net::SMTP=GLOB(0xb79ce68)>>>
Net::SMTP=GLOB(0xb79ce68)>>> Bugzilla has received a request to create a user account
Net::SMTP=GLOB(0xb79ce68)>>> using your email address (xxxxxx@gmail.com).
Net::SMTP=GLOB(0xb79ce68)>>>
Net::SMTP=GLOB(0xb79ce68)>>> To continue creating an account using this email address, visit the
Net::SMTP=GLOB(0xb79ce68)>>> following link by April 8, 2011 at 16:05 CST:
Net::SMTP=GLOB(0xb79ce68)>>>
Net::SMTP=GLOB(0xb79ce68)>>> token.cgi?t=FegLL6OpYM&a=request_new_account
Net::SMTP=GLOB(0xb79ce68)>>> PRIVACY NOTICE: Bugzilla is an open bug tracking system. Activity on most
Net::SMTP=GLOB(0xb79ce68)>>> bugs, including email addresses, will be visible to the public. We recommend
……
Net::SMTP=GLOB(0xb79ce68)>>> .
Net::SMTP=GLOB(0xb79ce68)<<< 250 Ok: queued as Net::SMTP=GLOB(0xb79ce68)>>> QUIT
Net::SMTP=GLOB(0xb79ce68)<<< 221 Bye job completed TheSchwartz::work_once found no jobs 到QQ Mail也看到发送成功的邮件,在Gmail也收到了邮件,到此才算基本成功。 Parameters -> Email里面我的基本几项数据如下
mailfrom : xxxxxx@qq.com // 出现问题就是这里填的不标准
use_mailer_queue : on
smtpserver : smtp.qq.com
smtp_username : xxxxxx@qq.com
smtp_password : ******

如果mail_delivery_method你选择SendMail或者Test,那么SMTP相关的参数就可以不用填了,如果你是通过SSL加密的SMTP链接,应该是要新装插件了

P.S. 关于use_mailer_queue的信息请挪步http://www.bugzilla.org/releases/4.0/release-notes.html#v34_feat_async

在Linux上安装Bugzilla v4

首先去官网http://www.bugzilla.org/下载Stable Release (4.0)
然后可以浏览下手册http://www.bugzilla.org/docs/4.0/en/html/

现在重点关注http://www.bugzilla.org/docs/4.0/en/html/installation.html
发现依赖于Perl,Database Engine,Web Server,mod_perl,Perl Modules,Mail Transfer Agent (MTA)

1、Perl在一般的Linux发行版上都有,只需要按照安装手册检测下版本是不是支持Bugzilla

2、数据库比较独立,Bugzilla支持的任意一种数据库都可以正常工作,这里我采用的是MySQL v5.5,以前有安装过。安装方法可以参考下http://lucane.iteye.com/blog/866355
安装好之后能正常登录就可以,等会来创建数据库实例和用户,因为bugzilla需要

3、Web Server目前只有Apache是官网推荐的,但你可以用其他的Server,如果用其他的Server安装Bugzilla成功,请别忘了写封邮件告诉Bugzilla团队和我。
这里我用的Apache 2.2.17源码编译安装,还算顺利,就是启动的时候遇到点问题,后来解决了
安装成功并启动成功的情况下,在浏览器下输入http://localhost/,会出现It works!的提示,说明Apache安装完成

4、mod_perl是Apache处理Perl的模块,我这里是采用动态插件的方式集成到Apache的,即Dynamic Shared Object方式,有人推荐静态编译的方式编译到Apache源码中,那样效率高。我这里就不需要了,如果要采取静态编译方式请参考官方文档,总之我们的目的是要使我们的Web Server具有处理Perl的能力。
去http://perl.apache.org/download/index.html下载mod_perl-2.0.5,然后编译安装
sudo perl Makefile.PL MP_APXS=$APACHE_HOME/bin/apxs
MP_APXS后面的这些参数会把已经安装好的Apache信息传递给编译程序,这样它就知道Apache在哪,
编译出来的mod_perl.so它就会默认放到$APACHE_HOME/modules文件夹下,不会随意给你放个地方

我在编译的时候出现了一个错误,就是提示系统缺少libperl.so文件
我到/usr/lib下查看,发现真没有这个文件,但是有个libperl.so.5.10.1文件,
于是我做了个软链接,如下
/usr/lib$ sudo ln -sv libperl.so.5.10.1 libperl.so
再次编译,成功

编译好之后就是配置Apache了,需要把mod_perl集成到Apache当中,在$APACHE_HOME/conf/httpd.conf
搜索Dynamic Shared Object (DSO) Support,会看到下方不远处有个例子
(在httpd.conf这个配置文件中#符号表示注释掉的内容,这里是给我们参考的)
# Example:
# LoadModule foo_module modules/mod_foo.so

我们只需要增加一行就行,
LoadModule perl_module modules/mod_perl.so
这样就把perl_module集成进来了,后面modules/mod_perl.so是物理位置,
也就是你应该在$APACHE_HOME/modules下看到有个mod_perl.so文件

保存修改好的配置文件,重启Apache(./apachectl -k stop,sudo ./apachectl -k start),
如果Apache还能正常启动就说明刚刚修改的配置文件的语法是正确的,我们就可以进行下一步的工作了

————————-
另外插个小问题,我在用./apachectl -k stop关闭Apache的时候,Apache并没有真正关闭,后来我直接kill进程了
貌似网络上也有人遇到我这个问题,谁知道原因烦请告知我一声
————————-

5、开始安装Bugzilla,Bugzilla是用Perl写的,我们机器上的Perl可能不是最完整的,我们安装时缺少什么模块都可以现装上去
把下载的bugzilla解压到硬盘某处,比如/var/www/或者/home/下的文件夹,不要放到系统或者临时文件夹下,这样都不好
为了安全考虑的话一般部署都会给Web Server单独的用户,单独的权限,单独存储空间

我这里是把bugzilla放在我用户下的一个文件夹下的(~/dev/a/bugzilla-4.0),然后做了个软链接到Apache的内容文件夹($APACHE_HOME/htdocs)下
$ ls -lh
总计 8.0K
lrwxrwxrwx 1 guohai guohai 31 2011-03-31 21:58 bugzilla -> ~/dev/a/bugzilla-4.0

我给~/dev/a/bugzilla-4.0文件夹的权限是我当前用户的权限,这个实际上很重要,后面遇到问题就跟权限有关

还要在Apache httpd.conf中发布bugzilla,增加段配置,看看已有的配置文件就知道该加哪了,$APACHE_HOME这个请替换成自己相应的路径


Options ExecCGI FollowSymLinks
AllowOverride Limit
DirectoryIndex index.cgi
Order allow,deny
Allow from all

懂的人就知道这段配置的意思,不懂的人就先这样加在里面,只要是相同版本的Apache应该是不会出错的

还要增加AddHandler cgi-script .cgi这个,一般默认都是开启了的,在httpd.conf下搜索下,如果没有开启就把前面的#符号去掉开启该功能就行

这样的话,我们期望访问bugzilla的地址就变成了http://localhost/bugzilla/

现在来正式开始安装Bugzilla程序
先运行~/dev/a/bugzilla-4.0$ ./checksetup.pl –check-modules
它会提示缺少哪些模块,比如Checking for DBD-Pg (v1.45) not found
凡是not found都是目前系统上不具有的模块,但是我们只需要先安装必须的模块,缺少的模块和安装方式上面都有写出来
一般我们只需要执行类似的命令sudo perl install-module.pl TheSchwartz就可以安装完成,很好理解
我在安装的时候最开始安装MySQL数据的模块,结果提示错误,可能是依赖于其他的模块,所以我先把其他模块安装完了再去安装MySQL的就可以了
如果出现安装错误之类的问题,只需要认真看看提示,一般都可以解决

必须的模块都安装完成之后就可以配置了,简单的来说就是些数据库链接的配置
参考http://www.bugzilla.org/docs/4.0/en/html/configuration.html
执行下./checksetup.pl,现在不带后面的check-modules参数
它会帮我们生成$BUGZILLA_HOME/localconfig这个文件,打开看看就知道是些比如数据库机器地址,数据库名字,密码等等

重点有两个参数需要注意
$webservergroup = ‘yourgroupname’;
$db_sock = ‘/etc/mysql/mysql.sock’;
上面一个就是说Web Server是以什么组的权限运行的,你这里就以这个组,尽量不要留空,留空很危险
下面的一个就是Mysql数据库本地连接相关的,可以加速本地连接性能,可以参考MySQL的安装,如果留空的话,Bugzilla就以默认的路径去查找,
如果找不到就可能会报数据库连接错误,所以查查MySQL的安装配置就明白了

这些都配置好了我们是不是可以重启Apache开始使用Bugzilla了,还不可以,因为刚刚只说了下数据库的连接,我们还并没有实际的创建数据库。
很简单,创建个用户(bugzilla),当然还有密码,创建个数据库(bugbase),然后把相应的权限授予创建的这个用户

这些都有了之后我们才能填写localconfig文件相应的内容,重启Apache,再执行遍~/dev/a/bugzilla-4.0$ ./checksetup.pl
这才是真正的安装,它会连接数据库创建表等等操作,如果没有错误的话会提示你创建管理员账户,一般输入电子邮件,名字,密码之类的就可以

然后我们高高兴兴的去打开浏览器去看看Bugzilla

这个时候运气好的人可能会发现真能够访问了,但是运气不好的人一般都会给出403错误,木有权限,Forbidden几个大字搞的你没有脾气
我是参考了下面的才解决的
http://support.modwest.com/content/2/181/en/after-i-install-bugzilla-i-get-error-403-forbidden.html

去Bugzilla目录下执行
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
find . -name \*.cgi -exec chmod 755 {} \;
find . -name \*.pl -exec chmod 755 {} \;
find . -name \*.sh -exec chmod 755 {} \;

但是在这之前需要说明点东西,可能会对理解这个权限有点帮助

首先,Apache本身是有运行权限的,搜索下httpd.conf,你会找到如下这段
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User guohai
Group guohai

它建议你单独创建用户和组,并在这里指定,然后以root权限运行Apache,它会自动帮助你切换过来

也就是我这里运行Apache的用户和组都是guohai,并且Bugzilla的localconfig中的$webservergroup也是guohai
并且Bugzilla目录下文件的用户和组都是guohai

这些都正确了之后现在应该是可以访问的了,能看到Bugzilla的页面就算告一段落了,但是还没有完。

因为Bugzilla当中有很多邮件通知,所以还需要配置MTA之类的东西,
最简单的方法检测目前操作系统环境是否已经满足该条件的就是在Bugzilla上注册一个新用户,
如果不出现错误就表示目前满足条件

我的机器上面出现错误,是因为没有装MTA的原因,我就装了个SendMail,大家可以参考其他相关文档。
官方有个简短有效的说法,就说你如果能用mail命令成功发邮件的话,那么Bugzilla也就是可以用的
错误如下:
There was an error sending mail from ‘bugzilla-daemon’ to ‘guohai@mailwalk.com’:error when closing pipe to /usr/sbin/sendmail: Broken pipe
Traceback:
at Bugzilla/Mailer.pm line 186
Bugzilla::Mailer::MessageToMTA(…) called at Bugzilla/Token.pm line 90
Bugzilla::Token::issue_new_user_account_token(…) called at /home/guohai/apps/volcano/apache/htdocs/bugzilla/createaccount.cgi line 73

单独运行下/usr/sbin/sendmail,提示:
Please install an MTA on this system if you want to use sendmail!

在修改配置的时候也出现了错误,因为修改配置信息,要往一个文件($BUGZILLA_HOME/data/params)里面写东西,文件需要有可写权限
~/dev/a/bugzilla-4.0/data$
-rw-rw-r– 1 guohai guohai 4.0K 2011-04-01 00:36 params
类似这些错误只需要根据错误提示给权限就可以了

还有个功能如果需要开启的话就还要改配置,那就是use_mailer_queue,也就是让通知邮件在后台以异步方式发送,一般用户多的系统都要开启该功能
但是该功能依赖于一个模块,否则就会如下出错
The new value for use_mailer_queue is invalid: Using the job queue requires that you have certain Perl modules installed. See the output of checksetup.pl for more information.

详情也可一参考http://www.bugzilla.org/docs/tip/en/html/api/jobqueue.html

编译jobqueue.pl
~/dev/a/bugzilla-4.0$ ./jobqueue.pl
Base class package “TheSchwartz” is empty.
(Perhaps you need to ‘use’ the module which defines that package first,
or make that module available in @INC (@INC contains: . lib/i686-linux-gnu-thread-multi lib /etc/perl /usr/local/lib/perl/5.10.1 /usr/local/share/perl/5.10.1 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl).
at Bugzilla/JobQueue.pm line 30
BEGIN failed–compilation aborted at Bugzilla/JobQueue.pm line 30.
Compilation failed in require at Bugzilla/JobQueue/Runner.pm line 36.
BEGIN failed–compilation aborted at Bugzilla/JobQueue/Runner.pm line 36.
Compilation failed in require at ./jobqueue.pl line 30.
BEGIN failed–compilation aborted at ./jobqueue.pl line 30.

发现缺少TheSchwartz模块
这个时候最简单的就是检测下哪些模块没有,之前我们已经执行过这个操作./checksetup.pl –check-modules

那就按照提示安装TheSchwartz模块
sudo perl install-module.pl TheSchwartz
发现还是报错,因为它还依赖于另一个模块
Can’t locate object method “new” via package “Bugzilla::JobQueue::Runner” at ./jobqueue.pl line 32.
果断看出是Bugzilla::JobQueue::Runner这个类不正确,或没有,犯下API发现
Bugzilla::JobQueue::Runner – A class representing the daemon that runs the job queue.
就说明还依赖另外一个模块,定位到Daemon::Generic
那就再安装Daemon模块sudo perl install-module.pl Daemon::Generic

编译 ./jobqueue.pl通过
启动 ./jobqueue.pl start
当然你也可以让jobqueue随操作系统开机启动,可以参照文档做些设置

这样就可以开启Bugzilla的use_mailer_queue功能了

这样才算基本完成了
不过还有一些功能需要,比如设置通知邮件的地址,就是说这些通知邮件是由哪个邮箱发出的,
有人可以把他定位到Gmail等第三方邮箱上,我目前还是个无效的邮件地址发出去的邮件,bugzilla-daemon@localhost6.localdomain6
这种邮件可能会被些其它邮箱拒绝

建议大家安装的时候还是先读读官方指南,我这里只是把出现的问题及解决办法记录下了,看起来不是那么顺畅。

问题列表

生活的一半是遇到问题,另一半是解决问题

问题也许很简单,但问题毕竟是问题

1、xNix下Apache 2.2启动
guoh@KNIGHT:~/apps/volcano/apache/bin$ ./apachectl -k start
(13)Permission denied: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs

只需要以$sudo ./apachectl -k start运行即可,没有别人写的那样复杂,httpd的own和group也不需要都为root用户

http://callaly.net/blog/archives/94.html

2、Perl的DateTime模块装不上
perl install-module.pl DateTime
CPAN.pm: Going to build D/DR/DROLSKY/DateTime-0.66.tar.gz
Can’t locate Module/Build.pm in @INC (@INC contains: /home/allcom/volcano/bugzilla-4.0/lib/i386-linux-thread-multi /home/allcom/volcano/bugzilla-4.0/lib /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl /usr/lib/perl5/5.8.8/i386-linux-thread-multi /usr/lib/perl5/5.8.8 .) at Build.PL line 5.
BEGIN failed–compilation aborted at Build.PL line 5.
Warning: No success on command[/usr/bin/perl Build.PL ]
DROLSKY/DateTime-0.66.tar.gz
/usr/bin/perl Build.PL — NOT OK
Skipping test because of notest pragma
Running Build install
Make had some problems, won’t install
Could not read metadata file. Falling back to other methods to determine prerequisites

我是在安装Bugzilla的时候碰上的,解决办法是先装perl install-module.pl DateTime::Locale这个

3、Undefined subroutine &Math::Random::Secure::irand called at Bugzilla/Util.pm line 577, line 522.
解决办法是rm -rf lib/Math/Random/Secure*,这个目录是Bugzilla下的目录,这个是从推特上看来的

4、64位Ubuntu安装Android SDK后执行adb emulator之类的命令时提示“No such a file or directory”,中文“没有那个文件或目录”
有可能是没有安装ia32-libs,所以只需要sudo apt-get install ia32-libs应该就可以

5、Can’t update table ‘tablek’ in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
触发器通过UPDATE语句更新本表刚刚插入或者修改的数据会造成循环引用
下面这样写就可以,相当于一个拦截器,AOP之类的思想吧

CREATE trigger insert_before_tablek
    before
        insert on tablek
    for each row
        begin
            set new.status = 25;
        end

6、/bin/bash: AR@:找不到命令
编译安装SysBench-0.4.12
sudo ./configure –prefix=SYSBENCH_INSTALLATION_HOME –with-mysql-includes=MYSQL_INSTALLATION_HOME/include –with-mysql-libs=MYSQL_INSTALLATION_HOME/lib

sudo make
前面遇到的几个小问题都解决了,但遇到个新的问题

Making all in mysql
make[3]: 正在进入目录 `/home/aristel/sysbench-0.4.12/sysbench/drivers/mysql’
rm -f libsbmysql.a
/bin/bash: AR@:找不到命令
make[3]: *** [libsbmysql.a] 错误 127
make[3]:正在离开目录 `/home/aristel/sysbench-0.4.12/sysbench/drivers/mysql’
make[2]: *** [all-recursive] 错误 1
make[2]:正在离开目录 `/home/aristel/sysbench-0.4.12/sysbench/drivers’
make[1]: *** [all-recursive] 错误 1
make[1]:正在离开目录 `/home/aristel/sysbench-0.4.12/sysbench’
make: *** [all-recursive] 错误 1

AR@找不到命令,这个感觉有可能是Make脚本中参数或什么没有替换对什么的,但不知道怎么改,看不懂Makefile
机器中目前ar命令是存在的
目前没有找到解决办法

7、MySQL类似这种语句写法SELECT value FROM paras WHERE pkey = ‘freeze’ AND domain = domain;

create table paras (value varchar(50), pkey varchar(50), domain varchar(50));

这个问题浪费了我很不少时间,因为在Linux下面,又是在存储过程中,并且是封装了异常的存储过程中,异常信息被吞掉了
调试工具也不给力,所以基本是人工看的

CREATE PROCEDURE tdemo(domain VARCHAR(50))
BEGIN
    DECLARE v_value VARCHAR(50);
    SELECT value FROM paras WHERE pkey = 'freeze' AND domain = domain INTO v_value;
END

这个过程编译通过,执行的时候报了下面的错误

ERROR 1172 (42000): Result consisted of more than one row

想想也知道是返回了多条数据到INTO,这是错误的
但为什么会返回多条数据呢?明明后面限制了
问题就处在存储过程的那个入参domain上,它和表的一个字段名一模一样的了
这时候MySQL就认为这两个始终相同,于是语句变成了下面这样子

SELECT value FROM paras WHERE pkey = 'freeze';

所以就错了

存储过程中变量命名也是值得考究的一个问题,当存储过程太长了时候是件很痛苦的事情
有人这么做
入参变量加IN_前缀
出参变量加OUT_前缀
零时变量加TMP_前缀
整形变量加I_前缀
字符串变量加S_前缀
时间日期变量加D_前缀

另外为了简单,避免出错,参数不要和字段一模一样

8、Java连接SQLServer,不说了,直接上最新驱动
时隔多年不碰SQLServer,突然要偶弄SQLServer存储过程,还给了偶三个最老的JAR包
连接池一上,错误警告百出,坑爹啊
直接下载新版驱动
http://msdn.microsoft.com/zh-cn/data/aa937724.aspx
然后设置,新驱动的连接串不一样
http://blogs.msdn.com/b/jdbcteam/archive/2007/06/15/java-lang-classnotfoundexception-com-microsoft-jdbc-sqlserver-sqlserverdriver.aspx
貌似驱动还有中英文的区分,我中文数据库随便用了个英文的好像也可以
如下列举的是可能出现的问题:
com.microsoft.jdbc.sqlserver.SQLServerConnection@9980d5 threw an Error when we tried to check its default holdability. This is probably due to a bug in your JDBC driver that c3p0 can harmlessly work around (reported for some DB2 drivers). Please verify that the error stack trace is consistentwith the getHoldability() method not being properly implemented, and is not due to some deeper problem. This message will not be repeated for Connections of type com.microsoft.jdbc.sqlserver.SQLServerConnection that provoke errors of type java.lang.AbstractMethodError when getHoldability() is called.
和C3P0一起用的时候

java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]Invalid parameter binding(s).

还有诸如什么切换语言之类的警告

9、在Ubuntu使用Wireshark时,Interface为空的,什么都没有
dumpcap: There are no interfaces on which a capture can be done
需要用root权限开启这个软件就好,即sudo
其他Linux应该也如此

有感于Android Calendar开发

Android虽然看起来很火,但是API可以说还不是很完善,变化大,有些功能还暂时木有提供。
比如Calendar相关的接口,目前还不够稳定,所以还没有对外开放,在模拟器上都还没有CalendarProvider和Calendar,虽然可以想尽办法给他安装上去,但还是有些问题。
前段时间为了开发基于Calendar的第三方应用重新编译了Calendar加入到android.jar,后来进行了一半没有这样做了,干脆直接自己写,虽然也是照着Calendar和CalendarProvider源码抄过来。

由于开放的原因,目前设备制造商是各自为政来定制自己的Calendar应用,比如Moto Milestone就必须要在登录Google账户的前提下才能使用Calendar,HTC、Samsung以及Huawei等等都有了一个自己内置的账户,所以他们把Calendar建立在自己的内置账户上,手机不用登录Google账户也可以使用Calendar,这样基于Calendar的第三方应用就格外难实现,又要不损坏原有系统的数据,保持原有功能能用,又要基于原有系统的数据开发自己的功能。

目前在参考了另外一些应用的实现之后,算是对这些情况有些了解了,等开发完成再整理再来完整的分析下。

Google Voice对中国用户开放了

不知是今天还是昨天,发现Gmail当中弹出了一个Call的提示信息框,翻译出来的意思就是说2011年用Gmail Call对美国和加拿大打电话免费,对其他地方打电话享受极大的折扣价格,what’s out,这不就是Google Voice吗?以前不对中国的用户开放,注册的时候还找代理什么的来完成,现在居然对中国用户开放了。这也算是一大消息吧。于是今天中午吃完饭想来试试看,看看是不是假的,愚人节还没有到吧。刚好目前在美帝国的一个同学在QQ上出现了,问他要了个电话,拨了过去(他接通了,但是在公司没有耳机和麦,所以测试不出来通话效果),但是这足以证明这个东西对中国人开放了,而且没有受到无敌防火墙的拦截。在国内打其他的电话价格是0.02$每分钟,换算过来还是很划算的,还可以发短信,感兴趣的可以试试。
如图
google-voice-open-up-to-china