neuhalfen.name

A random collection of posts

Creating and Installing an Solaris 11 IPS Package for the Zabbix Client and Server

Permalink

Besides several Linux VMs, I am using Solaris 11 for some of my home-tinkering systems. After an undetected three day outage of my mail server, I decided to install a monitoring solution for my systems.

Unwilling to go through the configure, make, make install triple jump every time I install a system, I set out to create a local IPS repository to serve my needs (and packages).

This tutorial consists of multiple steps

  1. Create and set up a local IPS in a dedicated zone
  2. Create a simple IPS package
  3. Create the Zabbix package and publish it (this article).

Preparations

Download the Zabbix source and extract it.

You also will need the mysql headers, NET-SNMP, libcurl, and gmake (see here). We will install them shortly.

Sun Studio

To build you’ll need Oracle Solaris Studio.

Just follow the howto on Kuldip’s Blog

Required Packages

The pkg equivalent to yum/rpm whatprovides is pkg search -a. For example, to find out which package provides mysqld_error.h call

pkg search -a -r mysqld_error.h
INDEX ACTION VALUE PACKAGE
basename file usr/mysql/5.1/include/mysql/mysqld_error.h pkg:/database/mysql-51@5.1.37-0.151.0.1
basename file usr/mysql/5.0/include/mysql/mysqld_error.h pkg:/database/mysql-50@5.0.91-0.151.0.1
pkg search -r -a curl.h
INDEX ACTION VALUE PACKAGE
basename file usr/include/curl/curl.h pkg:/web/curl@7.21.1-0.151.0.1

Now install the needed packages:

pkg install pkg:/text/gnu-sed pkg:/text/gnu-grep pkg:/system/management/snmp/net-snmp pkg:/developer/build/gnu-make pkg:/database/mysql-51 pkg:/web/curl pkg:/text/gnu-grep pkg:/database/mysql-51/library

Building

Agent

cd $HOME/zabbix/zabbix-1.8.8
DEST=$HOME/zabbix_install/agent/root
[ -d $DEST ] && rm -rf $DEST
mkdir -p $DEST
make clean
./configure --disable-server --with-mysql=/usr/mysql/5.1/bin/mysql_config --with-net-snmp --without-jabber --with-libcurl --enable-agent --prefix=$DEST/usr
make install
mkdir -p $DEST/etc/zabbix
cp ./misc/conf/zabbix_agent.conf $DEST/etc/zabbix/zabbix_agent.conf.example
cp ./misc/conf/zabbix_agentd.conf $DEST/etc/zabbix/zabbix_agentd.conf.example
mkdir -p $DEST/usr/share/doc/zabbix
cp COPYING $DEST/usr/share/doc/zabbix/LICENSE

Server

cd $HOME/zabbix/zabbix-1.8.8
DEST=$HOME/zabbix_install/server/root
[ -d $DEST ] && rm -rf $DEST
mkdir -p $DEST
make clean
./configure --enable-server --with-mysql=/usr/mysql/5.1/bin/mysql_config --with-net-snmp --without-jabber --with-libcurl --disable-agent --prefix=$DEST/usr
# This will install zabbix into @$HOME/zabbix_install@.
make install
mkdir -p $DEST/etc/zabbix
cp ./misc/conf/zabbix_server.conf $DEST/etc/zabbix/.
mkdir -p $DEST/usr/share/doc/zabbix
cp COPYING $DEST/usr/share/doc/zabbix/LICENSE
# To ease server-setup, copy the @SQL@ scripts to create the @zabbix_server@ DB
# and create a small README:
mkdir -p $DEST/usr/share/doc/zabbix/install
cp -R create/{schema,data} $DEST/usr/share/doc/zabbix/install/.
cat <<EOF> $DEST/usr/share/doc/zabbix/install/README.INSTALL
Linker
=======
If you have issues with the mysql libraries not being found by the rt linker try (as root):
crle -u -l /usr/mysql/5.1/lib/mysql
Database
==========
Setting up the database for MySQL (Zabbix Server only):
(from http://www.zabbix.com/documentation/1.8/manual/installation/installation_from_source)
For MySQL:
shell> mysql -u<username> -p<password>
mysql> create database zabbix character set utf8;
mysql> quit;
shell> cd create/schema
shell> cat mysql.sql | mysql -u<username> -p<password> zabbix
shell> cd ../data
shell> cat data.sql | mysql -u<username> -p<password> zabbix
shell> cat images_mysql.sql | mysql -u<username> -p<password> zabbix
EOF

Creating the IPS-package

Common

A common package that creates the log-directory and the zabbix user

mkdir -p $HOME/zabbix_install/common
cd $HOME/zabbix_install/common
cat <<EOF>MANIFEST.meta
set name=maintainer value="Jens Neuhalfen <jens@neuhalfen.name>"
set name=upstream_url value="http://www.zabbix.com/"
set name=description value="Zabbix Common Files"
set name=variant.arch value=i386
group groupname=zabbix
user ftpuser=false gcos-field="Zabbix User" group=zabbix username=zabbix
EOF
cat <<EOF> MANIFEST.files
dir group=zabbix mode=0750 owner=zabbix path=var/adm/zabbix timestamp=20110927T234703Z
EOF
cat <<EOF>send.sh
#!/bin/bash
IPS_SERVER=http://ips.local.neuhalfen.name:80
export NAME=application/zabbix/common
export VERSION=1.8.8
export BUILD=4
export PACKAGE=\${NAME}@\${VERSION}-\${BUILD}
cd $(pwd)
export PKGSEND="pkgsend -s \$IPS_SERVER"
eval \`\$PKGSEND open \$PACKAGE\`
\$PKGSEND include -d root MANIFEST.meta
\$PKGSEND include -d root MANIFEST.files
\$PKGSEND close
EOF

Agent

cd $HOME/zabbix_install/agent
mkdir -p root//var/svc/manifest/application
# see here for more info: http://cuddletech.com/blog/pivot/entry.php?id=182
cat <<EOF>root/var/svc/manifest/application/zabbix-agentd.xml
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="zabbix-agent">
<service name="application/zabbix-agent" type="service" version="5">
<create_default_instance enabled="false"/>
<single_instance/>
<dependency name="fs-local" grouping="require_all" restart_on="none" type="service">
<service_fmri value="svc:/system/filesystem/local"/>
</dependency>
<dependency name='loopback'
grouping='require_all'
restart_on='error'
type='service'>
<service_fmri value='svc:/network/loopback:default'/>
</dependency>
<method_context>
<method_credential user='zabbix' group='zabbix' />
<method_environment>
<envvar name="PATH" value="/bin:/sbin:/usr/bin:/usr/sbin" />
</method_environment>
</method_context>
<exec_method type="method" name="start" exec="/usr/sbin/zabbix_agentd" timeout_seconds="60"/>
<exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"/>
<stability value="Unstable"/>
<template>
<common_name>
<loctext xml:lang="C">Zabbix Agent</loctext>
</common_name>
</template>
</service>
</service_bundle>
EOF
cat <<EOF>MANIFEST.meta
license ./usr/share/doc/zabbix/LICENSE license=GPLv2
set name=maintainer value="Jens Neuhalfen <jens@neuhalfen.name>"
set name=upstream_url value="http://www.zabbix.com/"
set name=description value="Zabbix Agent"
set name=variant.arch value=i386
depend fmri=SUNWcs type=require
depend fmri=system/library type=require
depend fmri=system/library/c++/sunpro type=require
depend fmri=library/zlib type=require
depend fmri=system/library/math type=require
depend fmri=consolidation/sfw/sfw-incorporation type=require
depend fmri=application/zabbix/common type=require
EOF
pkgsend generate root > MANIFEST.files
# The manifest gets imported by adding restart_fmri=...
gsed -i MANIFEST.files -e '/var\/svc\/manifest.*.xml/s/$/ restart_fmri=svc:\/system\/manifest-import:default/'
cat <<EOF>send.sh
#!/bin/bash
IPS_SERVER=http://ips.local.neuhalfen.name:80
export NAME=application/zabbix/agent
export VERSION=1.8.8
export BUILD=4
export PACKAGE=\${NAME}@\${VERSION}-\${BUILD}
cd $(pwd)
export PKGSEND="pkgsend -s \$IPS_SERVER"
eval \`\$PKGSEND open \$PACKAGE\`
\$PKGSEND include -d root MANIFEST.meta
\$PKGSEND include -d root MANIFEST.files
\$PKGSEND close
EOF

Do not forget to execute send.sh.

Configuration

This package includes a matching agent configuration for my systems.

mkdir -p $HOME/zabbix_install/agent_config
cd $HOME/zabbix_install/agent_config
mkdir -p root/etc/zabbix
cat <<EOF>root/etc/zabbix/zabbix_agentd.conf
LogFile=/var/adm/zabbix/zabbix_agentd.log
LogFileSize=5
DebugLevel=3
EnableRemoteCommands=0
Server=zabbix.local.neuhalfen.name
#Hostname=Zabbix server
DisablePassive=1
DisableActive=0
RefreshActiveChecks=120
AllowRoot=0
EOF
cat <<EOF>MANIFEST.meta
set name=maintainer value="Jens Neuhalfen <jens@neuhalfen.name>"
set name=upstream_url value="http://www.zabbix.com/"
set name=description value="Zabbix Agent Configuration for neuhalfen.name"
set name=variant.arch value=i386
depend fmri=application/zabbix/agent type=require
EOF
pkgsend generate root > MANIFEST.files
# Do not overwrite the config file with updates
gsed -i MANIFEST.files -e '/file .*etc/s/$/ preserve=renamenew/'
cat <<EOF>send.sh
#!/bin/bash
IPS_SERVER=http://ips.local.neuhalfen.name:80
export NAME=application/zabbix/agent-config
export VERSION=1.8.8
export BUILD=4
export PACKAGE=\${NAME}@\${VERSION}-\${BUILD}
cd $(pwd)
export PKGSEND="pkgsend -s \$IPS_SERVER"
eval \`\$PKGSEND open \$PACKAGE\`
\$PKGSEND include -d root MANIFEST.meta
\$PKGSEND include -d root MANIFEST.files
\$PKGSEND close
EOF

Server

cd $HOME/zabbix_install/server
mkdir -p root//var/svc/manifest/application
cat <<EOF>root/var/svc/manifest/application/zabbix-server.xml
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="zabbix-server">
<service name="application/zabbix-server" type="service" version="5">
<create_default_instance enabled="false"/>
<single_instance/>
<dependency name="fs-local" grouping="require_all" restart_on="none" type="service">
<service_fmri value="svc:/system/filesystem/local"/>
</dependency>
<dependency name='loopback'
grouping='require_all'
restart_on='error'
type='service'>
<service_fmri value='svc:/network/loopback:default'/>
</dependency>
<method_context>
<method_credential user='zabbix' group='zabbix' />
<method_environment>
<envvar name="PATH" value="/bin:/sbin:/usr/bin:/usr/sbin" />
</method_environment>
</method_context>
<exec_method type="method" name="start" exec="/usr/sbin/zabbix_server" timeout_seconds="60"/>
<exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"/>
<stability value="Unstable"/>
<template>
<common_name>
<loctext xml:lang="C">Zabbix Server</loctext>
</common_name>
</template>
</service>
</service_bundle>
EOF
cat <<EOF>MANIFEST.meta
license ./usr/share/doc/zabbix/LICENSE license=GPLv2
set name=maintainer value="Jens Neuhalfen <jens@neuhalfen.name>"
set name=upstream_url value="http://www.zabbix.com/"
set name=description value="Zabbix Server"
set name=variant.arch value=i386
depend fmri=SUNWcs type=require
depend fmri=system/library type=require
depend fmri=system/library/c++/sunpro type=require
depend fmri=library/zlib type=require
depend fmri=system/library/math type=require
depend fmri=consolidation/sfw/sfw-incorporation type=require
depend fmri=database/mysql-51/library type=require
depend fmri=application/zabbix/common type=require
EOF
pkgsend generate root > MANIFEST.files
# The manifest gets imported by adding restart_fmri=...
gsed -i MANIFEST.files -e '/var\/svc\/manifest.*.xml/s/$/ restart_fmri=svc:\/system\/manifest-import:default/'
cat <<EOF>send.sh
#!/bin/bash
IPS_SERVER=http://ips.local.neuhalfen.name:80
export NAME=application/zabbix/server
export VERSION=1.8.8
export BUILD=4
export PACKAGE=\${NAME}@\${VERSION}-\${BUILD}
cd $(pwd)
export PKGSEND="pkgsend -s \$IPS_SERVER"
eval \`\$PKGSEND open \$PACKAGE\`
\$PKGSEND include -d root MANIFEST.meta
\$PKGSEND include -d root MANIFEST.files
\$PKGSEND close
EOF

Do not forget to execute send.sh.

Web interface

SRC=$HOME/zabbix/zabbix-1.8.8
DEST=$HOME/zabbix_install/web/root
[ -d $DEST ] && rm -rf $DEST
mkdir -p $DEST
WEB_DEST=$DEST/var/zabbix/php-frontend
mkdir -p $WEB_DEST
cp $SRC/COPYING $WEB_DEST/LICENSE
cp -Rp $SRC/frontends/php $WEB_DEST
cat <<EOF>$WEB_DEST/zabbix.conf
# Create a symlink to /etc/apache2/2.2/conf.d/zabbix.conf
#
# Required modules: mod_php
#
Alias /zabbix /var/zabbix/php-frontend/php
<Location "/zabbix">
Options None
AllowOverride None
Order allow,deny
Allow from all
</Location>
EOF
cd $DEST/..
cat <<EOF>MANIFEST.meta
license $WEB_DEST/LICENSE license=GPLv2
set name=maintainer value="Jens Neuhalfen <jens@neuhalfen.name>"
set name=upstream_url value="http://www.zabbix.com/"
depend fmri=SUNWcs type=require
depend fmri=system/library type=require
depend fmri=system/library/c++/sunpro type=require
depend fmri=library/zlib type=require
depend fmri=system/library/math type=require
depend fmri=consolidation/sfw/sfw-incorporation type=require
depend fmri=database/mysql-51/library type=require
depend fmri=pkg:/web/server/apache-22/module/apache-php5 type=require
depend fmri=pkg:/web/php-52/extension/php-mysql type=require
depend fmri=pkg:/web/server/apache-22 type=require
EOF
pkgsend generate $DEST > MANIFEST.files
cat <<EOF>send.sh
#!/bin/bash
IPS_SERVER=http://ips.local.neuhalfen.name:80
export NAME=application/zabbix/web-ui
export VERSION=1.8.8
export BUILD=4
export PACKAGE=\${NAME}@\${VERSION}-\${BUILD}
cd $(pwd)
export PKGSEND="pkgsend -s \$IPS_SERVER"
eval \`\$PKGSEND open \$PACKAGE\`
\$PKGSEND include MANIFEST.meta
\$PKGSEND include -d $DEST MANIFEST.files
\$PKGSEND close
EOF

Do not forget to execute send.sh.

This and that

Error during deinstallation

Users and groups added in the manifest are not reference counted or anything by IPS. When a package gets deinstalled, all users/groups created by it are removed too. This can be cumbersome, esp. when you hit a bug in IPS: Removing a non-existing user causes a stacktrace:

root@ips-test:~# pkg uninstall -v application/zabbix/agent
Packages to remove: 1
Create boot environment: No
Rebuild boot archive: No
Changed fmris:
pkg://neuhalfen.name/application/zabbix/agent@1.8.5,5.11-1:20110927T132721Z -> None
Services:
None
PHASE ACTIONS
Removal Phase 1/26Action removal failed for 'zabbixa' (pkg://neuhalfen.name/application/zabbix/agent):
KeyError: ('zabbixa',)
pkg: An unexpected error happened during uninstall: ('zabbixa',)
Traceback (most recent call last):
File "/usr/bin/pkg", line 4225, in handle_errors
__ret = func(*args, **kwargs)
File "/usr/bin/pkg", line 4204, in main_func
return func(img, pargs)
File "/usr/bin/pkg", line 1476, in uninstall
return __api_execute_plan(op, api_inst)
File "/usr/bin/pkg", line 1001, in __api_execute_plan
api_inst.execute_plan()
File "/usr/lib/python2.6/vendor-packages/pkg/client/api.py", line 828, in execute_plan
self.__img.imageplan.execute()
File "/usr/lib/python2.6/vendor-packages/pkg/client/imageplan.py", line 1212, in execute
p.execute_removal(src, dest)
File "/usr/lib/python2.6/vendor-packages/pkg/client/pkgplan.py", line 366, in execute_removal
src.remove(self)
File "/usr/lib/python2.6/vendor-packages/pkg/actions/user.py", line 269, in remove
pw.removevalue(self.attrs)
File "/usr/lib/python2.6/vendor-packages/pkg/cfgfiles.py", line 267, in removevalue
self.password_file.removevalue(template)
File "/usr/lib/python2.6/vendor-packages/pkg/cfgfiles.py", line 172, in removevalue
del self.index[tuple(template[k] for k in self.keys)]
KeyError: ('zabbixa',)

Solution

Edit /etc/shaddow and add the user. In this case it would be zabbixa.

MySQL Libraries not found (configure)

Configure complains about missing mysql client libraries:

./configure ...
...
configure: error: Not found mysqlclient library

Solution

If you have gcc installed, force configure to use the Solaris compiler, else you will get errors when configure looks for the mysql client library.

export CC=/bin/cc

Also make sure you have /usr/mysql/5.1/bin/mysql_config installed (pkg:/database/mysql-51).

MySQL Libraries not found (runtime)

The runtime library path in Solaris 11 is currently not set up correctly for MySQL. Specifically, the MySQL libraries are not reachable by the rt linker:

./zabbix_server
ld.so.1: zabbix_server: fatal: libmysqlclient.so.16: open failed: No such file or directory
Killed

Solution

A solution would either to set LD_LIBRARY_PATH before executing zabbix_server

LD_LIBRARY_PATH=/usr/mysql/5.1/lib/mysql ./zabbix_server
zabbix_server [17315]: cannot open config file [/etc/zabbix/zabbix_server.conf] [No such file or directory]

Messing with LD_LIBRARY_PATH is not encouraged on Solaris. Instead use crle (configure runtime linking environment) to set up the system wide linker lookup path:

crle -u -l /usr/mysql/5.1/lib/mysql

I curently have no idea, how I can do this with ips.

Comments