How to Install Odoo 13 in CentOS 7

Odoo is currently one of the most popular tool for business purposes. It has a community edition, that allows managing ERP at very low cost. Odoo was previously known as OpenERP. Odoo requires to be installed on a dedicated server or VPS. Odoo 13 had come out on October, 2019. Odoo 14 hasn’t been released yet for production. I will have a straight forward how to on installing the latest Odoo 13 in CentOS 7.

Log in to your system and update

First step would be to login to your system and then update the system using yum.

ssh root@server_ip

You may check the CentOS version from the redhat release file using the following:

cat /etc/redhat-release

It should show you something like the following if you

CentOS Linux release 7.8.2003 (Core)

Now, you may try updating the system with yum

yum update -y

Once done, now install the EPEL repository as we need it to satisfy a couple of dependecies:

yum install epel-release

Install Python 3.6 packages and Odoo dependencies

We need Python 3.6 at least to run Odoo 13. Odoo 12 had support for Python 3.5, unfortunately, Odoo 13 doesn’t. We will use ‘Software Collection (scl)’ repository to install and use Python 3.6. To find the available Python versions in SCL, you may check the following:

SCL Repository for Python

Now, to install Python 3.6 using SCL, we first need to install the SCL repository for Centos:

yum install centos-release-scl

Once the SCL is loaded, now, you may install the python 3.6 using the following command:

yum install rh-python36

Once the Python is installed, now we will install several tools and packages for Odoo dependencies with the following command:

yum install git gcc nano wget nodejs-less libxslt-devel bzip2-devel openldap-devel libjpeg-devel freetype-devel

Create Odoo User

We now need to create a system user and group for Odoo and define a home directory to /opt/odoo

useradd -m -U -r -d  /opt/odoo -s /bin/bash odoo

You may use any username here, but remember to create the same username for the PostgreSQL as well.

Install PostgreSQL

CentOS base repository unfortunately, comes with Postgresql 9.2. But we want to use PostgreSQL 9.6 for our Odoo installation. You may check the available PostgreSQL for CentOS 7 using the following command:

yum list postgresql*

As by default CentOS 7 does not provide the PostgreSQL 9.6, we would use PostgreSQL official repository to download and install the 9.6 version.

First, we install the Postgres Yum Repository using the following command:

yum install https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

Now, you may install PostgreSQL 9.6 and related required packages using the following command:

yum install postgresql96 postgresql96-server postgresql96-contrib postgresql96-libs

Now, we need to initialize the postgres database and start it. You may do that using the following:

# Initialize the DB
/usr/pgsql-9.6/bin/postgresql96-setup initdb

# Start the database
systemctl start postgresql-9.6.service

Now you may enable Postgres to start when booting up using the systemctl enable command:

systemctl enable postgresql-9.6.service

Now, we need to create a database user for our Odoo installation. You may do that using the following:

su - postgres -c "createuser -s odoo"

Note: If you have created a different user for Odoo installation other than ‘odoo’ than you should change the username here as well.

Install Wkhtmltopdf

Wkhtmltopdf is a open source tool to make html in pdf format so that you may print pdf reports. This tool is used by Odoo and requires to be installed as dependency. CentOS 7 repository does not provide the latest version of this tool, and Odoo requires you to use the latest version. Hence, we require to download the latest version from the Wkhtmltopdf website and install it. To do that, you may first visit the page:

https://wkhtmltopdf.org/downloads.html

The page gives you the direct rpm download link for each version of CentOS/Ubuntu/Mac etc. Download the stable version for CentOS 7. At the time of writing, the URL for CentOS 7 x86_64 bit is the following:

https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox-0.12.6-1.centos7.x86_64.rpm

You may install this using the following:

cd /opt/
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox-0.12.6-1.centos7.x86_64.rpm
yum localinstall wkhtmltox-0.12.6-1.centos7.x86_64.rpm

Install and Configure Odoo 13

If you have come all through here, that means you are done with the all dependency installations before starting to download Odoo 13 source code. We will download Odoo 13 from it’s Github repo and use virtualenv to create an isolated python environment to install this python software.

First, login as odoo from root:

su - odoo

Clone the Odoo source code from Github repository:

git clone https://www.github.com/odoo/odoo --depth 1 --branch 13.0 /opt/odoo/odoo13

This will bring the Odoo 13 branch from the Odoo repository and put it inside the folder /opt/odoo/odoo13

Now, we need to enable software collections in order to access python binaries:

scl enable rh-python36 bash

Then we need to create a virtual environment to complete the installation:

cd /opt/odoo
python3 -m venv odoo13-venv

Now, you may activate the virtual environment you have just created:

source odoo13-venv/bin/activate

Now, we upgrade the pip and install the wheel library:

pip install --upgrade pip
pip3 install wheel

Once done, now we can using pip3 to install all the required Python modules from the requirements.txt file:

pip3 install -r odoo13/requirements.txt

Once the installation is complete, now we can deactivate the virtual environment and get back to the root user

deactivate && exit ; exit

If you think, you will create custom modules, you may now create it and give odoo the permission accordingly:

mkdir /opt/odoo/odoo13-custom-addons
chown odoo: /opt/odoo/odoo13-custom-addons

Now, we can fill up the odoo configuration file. First open the odoo.conf file:

nano /etc/odoo.conf

You may paste the following inside:

[options]
; This is the password that allows database operations:
admin_passwd = set_the_password_to_create_odoo_database
db_host = False
db_port = False
db_user = odoo
db_password = False
addons_path = /opt/odoo/odoo13/addons,/opt/odoo/odoo13-custom-addons
; You can enable log file with uncommenting the next line
; logfile = /var/log/odoo13/odoo.log

Please do not forget to change the password ‘set_the_password_to_create_odoo_database’ with a new strong password. This would be used to create Odoo databases from the login screen.

Create the systemd service file and start Odoo 13

Now, we will create a service file, to be able to start, stop and restart Odoo daemon. To do that, first create a service file using the following:

nano /etc/systemd/system/odoo13.service

and paste the following:

[Unit]
Description=Odoo13
Requires=postgresql-9.6.service
After=network.target postgresql-9.6.service

[Service]
Type=simple
SyslogIdentifier=odoo13
PermissionsStartOnly=true
User=odoo
Group=odoo
ExecStart=/usr/bin/scl enable rh-python35 -- /opt/odoo/odoo13-venv/bin/python3 /opt/odoo/odoo13/odoo-bin -c /etc/odoo.conf
StandardOutput=journal+console

[Install]
WantedBy=multi-user.target

Now, save the file and exit.

Now, you need to reload the systemd daemon to be able to read the latest changes you have made to services. To do that, run:

systemctl daemon-reload

Finally, now we can start Odoo 13 instance using the following command:

systemctl start odoo13

If you are interested to check the status of the instance, you may do this:

systemctl status odoo13
[root@hr ~]# systemctl status odoo13
● odoo13.service - Odoo13
   Loaded: loaded (/etc/systemd/system/odoo13.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-09-13 08:26:46 EDT; 23h ago
 Main PID: 24502 (scl)
   CGroup: /system.slice/odoo13.service
           ├─24502 /usr/bin/scl enable rh-python36 -- /opt/odoo/odoo13-venv/bin/python3 /opt/odoo/odoo13/odoo-bin -c /etc/odoo.conf
           ├─24503 /bin/bash /var/tmp/sclSWH04z
           └─24507 /opt/odoo/odoo13-venv/bin/python3 /opt/odoo/odoo13/odoo-bin -c /etc/odoo.conf

It show green active running, if everything worked out. If you see no error, you may now enable Odoo to start during the boot:

systemctl enable odoo13

If you would like to see the logs, you may either use the journal tools like the following:

journalctl -u odoo13

or uncomment the following line to log the debugs in /etc/odoo.conf

logfile = /var/log/odoo13/odoo.log

After making any change to /etc/odoo.conf, do not forget the restart the Odoo13 instance using systemctl.

Test the Installation

You may now test the installation using http://your_server_ip:8069. If everything worked, it should come up. If it doesn’t, you may try stopping your ‘firewalld’ to see if firewall is blocking the port or not:

systemctl stop firewalld

At Mellowhost, we provide Odoo installation and configuration assistance for absolute free of charge. If you are willing to try out any of our VPS for Odoo, you may do so and talk with us through the Live chat or the ticket for Odoo assistance.

Furthermore, Good luck.

How to List members of Distribution List in Zimbra

Problem Definition:

Zimbra provides the ability to use a distribution list, that allows you t add members to the list, and when you send mail to the distribution list, zimbra picks all the members and sends the same mail to all the members. This allows you to easily do group mailing or department wise mailing in a company. Zimbra admin panel does not allow you to list/download all the members in one page and download them. One option available from admin panel is to go to the details of the distribution list email address and from the Member pane, you may download the first page of the list. But the page does not allow you to move on. How can we download all the members of distribution list in Zimbra in one command?

How To List/Download Members of Distribution List in Zimbra

You may do it using the zimprov command given by zimbra. Here are the steps to do so:

~ su - zimbra
~ for i in `zmprov gadl` ; do zmprov gdl $i zimbraMailAlias zimbraMailForwardingAddress ; done

# if you want to store them in a file and download them:
for i in `zmprov gadl` ; do zmprov gdl $i zimbraMailAlias zimbraMailForwardingAddress ; done > /tmp/dd_users.txt

How to Add SRV Records

SRV are ‘service’ type records added in dns zone files. These are added to indicate a certain kind of service runs on a specific port, on a specific domain/subdomain. Many control panel, will give clear distinction to make you select the following items in a srv record:

  1. Protocol Type (TCP/UDP)
  2. Weight
  3. Port
  4. Target Host

Now, if by any chance, you are doing it in a panel like Cyberpanel, or some other zone files, it is highly likely you will have a way to distinct any of these. This simple How to, is for them specially.

If for example, your domain is ‘nanotools.com’, and you are trying to add something like autodiscover.nanotools.com with the following details:

Protocol: TCP
Weight: 0
Port: 443
Target Host: fortinet.yourmails.com

Let’s start with is going to be your ‘name’ field. Usually you would use ‘autodiscover’ as the name field, but in this case, you would have to use it with the protocol with a underscore and a dot (._) in front as following:

autodiscover._tcp

Next, you enter TTL, Prioriy as you would also do. But at the end, you have 3 more fields to enter, but you have one content/value section in your zone. For the SRV records, we fill it like the following:

weight port target_host

It means, we first give the weight, then a space, then the port, then a space, then the target hostname and then a dot to end the section. In our case, it would be:

0 443 fortinet.yourmails.com.

So the whole record going to be as following: (expects priority is 10)

autodiscover._tcp 3600 IN SRV 10 0 443 frotinet.yourmails.com.

How to Call a Controller Method from Tinker – Laravel

LARAVEL TINKER

If you love to debug and test things in an app shell like me, then you are also a big fan of Tinker in laravel. Tinker is the shell prompt for Laravel and can be used to test and run different commands in php inside the app. You may run the following to hop into the tinker shell in a laravel environment:

php artisan tinker

Once you hop in the tinker, you can call any model or run any php command from the shell.

HOW TO RUN CONTROLLER METHOD FROM TINKER

There are times, you might feel more interest into evaluating a large controller method. To run a controller method, we first need to enter the service container of laravel. Laravel providers a helper method called ‘app()’ to enter the service container. It can then use a method called ‘call’ to access and execute a method inside a controller namespace, like the following:

app()->call('App\Http\Controllers\AdminControllers@yourmethod');

Repace your controller name and the method name after @. One thing, you need to realize is that the method ‘call’ takes the method reference, not the function itself. That means, you can not add brackets () at the end of method name while giving it in the call method.

HOW TO PASS PARAMETERS TO CONTROLLER FROM TINKER

As discussed earlier, you are passing reference only, not the function, hence you can not pass parameters like we usually do in methods/functions. We need to pass this as an argument in array.

Here is a more constructive way to do this:

# let's make an instance of controller first, can be done using make method of service container
$controller = app()->make('App\Http\Controllers\AdminControllers');

# now let's call the method, inside the container, method name is 'getNewsByCatId'
app()->call([$controller, 'getNewsByCatId']);

# pass a parameter called id = 5
app()->call([$controller, 'getNewsByCatId'], ['id' => 5]);

How to get the string/part/word/text within brackets in Python using Regex

PROBLEM DEFINITION

For example, you have a string like the following:

[lagril] L.A. Girl Pro. Setting HD Matte Finish Spray

While you are scanning the line, you would like to extract the following word from it ‘lagril’, which you are interested in. How to do that?

GETTING TEXT WITHIN BRACKETS USING REGEX IN PYTHON

Our problem falls into a common string extraction problem we face in software engineering. We usually do this using Regular Expressions. Let’s build the regular expression logic first, using regex101.com

We need to find a string that starts with ‘[‘ bracket and ends with ‘]’ bracket, and in the middle, we expect alphanumeric word with small or capital letters, and they can be anything from 0 to any. So, this should be as simple as the following:

\[[A-Za-z0-9]*\]

Now, this should help us target the words that comes within bracket in a sentence/large string. But the trick to grab the text within the bracket is to group them. To use group in regex, we use () brackets without back slash in front. So if the regex is as following:

\[([A-Za-z0-9]*)\]

This will put the matching string in group 1. Now, how can you get what is in the group 1 of a regular expression engine? Let’s dive into python now:

# let's import regular expression engine first
import re

# our string
txt = '[lagril] L.A. Girl Pro. Setting HD Matte Finish Spray'

# our regex search would be as following:
x = re.search(r"\[([A-Za-z0-9]*)\]", txt)

# we know this will put the inner text in group 1. regex object that returned by re.search, has a method called 'group()' to catch the groups matches regex. You may use the following

x.group(1) # prints lagril

How to Install Zimbra Universal UI Theme on Zimbra 8.8.*

WHAT IS ZIMBRA UNIVERSAL UI

Zimbra universal UI is a theme by Zimbra, that is much more modern, professional looking theme, looks much near to Outlook theme. According to Zimbra, this is still in Beta since 2017, and not available anywhere unfortunately to install/download it from Zimbra until you are using one of the Zimbra Cloud by Zimbra Inc.

HOW DID WE MANAGE TO GET ZIMBRA UNIVERSAL UI?

Zeta Alliance had released an unofficial Zimbra 8.8.5 with the Zimbra Universal UI theme in it, that is downloadable here:

zcs-8.8.5_GA_2000.RHEL7_64.20171222124635.tgz

We installed it and tweaked the ZUU theme to be able to use in any Zimbra 8.8.* version available.

HOW TO INSTALL ZIMBRA UNIVERSAL UI

First keep a copy of zimbra folder:

rsync -vrplogDtH /opt/zimbra/jetty/webapps/zimbra /root/zimbra_backup

Then, download a copy of the zimbra UI from here:

wget https://mellowhost.com/downloads/zimbra-ui.tgz

Untar and migrate to zimbra folder:

tar -xvzf zimbra-ui.tgz
rsync -vrplogDtH zimbra/ /opt/zimbra/jetty/webapps/zimbra/

(Optional) This will install a new theme called ‘clarity’ which is our Zimbra Universal UI theme. You may either use this, and advise your users to choose this as a theme, or replace the default Harmony with this one. To do this, you may do the following:

cd /opt/zimbra/jetty/webapps/zimbra/skins
mv harmony harmony_old
ln -sf clarity harmony

This is done only by creating a softlink for old harmony to clarity theme

Now fix the permissions and restart zimbra to make everything work:

# Fix permission
/opt/zimbra/libexec/zmfixperms -v
service zimbra restart

You may now access Zimbra from your browser to see the new theme loading. If you are having trouble with the http access for your webmail, you may use the following to redirect your http webmail to https webmails

How to Fix zmconfigd failed in Zimbra – Starting zmconfigd…failed.

Sometimes, if you restart Zimbra, you see zmconfigd is not starting or saying it’s failed. You may also see the zmconfigd service is not running in the Zimbra admin panel. There are couple of common reasons why zmconfigd fails to start.

Disable IPv6

One reason of zmconfigd fails to start is IPv6, for some reason, it fails to route the IPv6 and fails to start. A quick solution to this problem is to disable ipv6 and restart zmconfigd. You may do this like the following:

#Edit your sysctl.conf file
nano /etc/sysctl.conf

# paste the following inside the file
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

# Save the file, and update sysctl in realtime
sysctl -p

# now try to restart zmconfigd
su - zimbra
zmconfigdctl restart

Now you can check the zmconfigd status with the following, to know if it’s running or not:

[root@mailapp ~]# cat /opt/zimbra/log/zmconfigd.pid
19722

If it returns an ID, it means the zmconfigd is running.

Netcat is not installed

Another reason of the error could be because nc is not installed in your system. Zimbra zmconfigd has a dependency on netcat package. Netcat is available through nmap-ncat in centos systems. You may run the following to install netcat:

yum install nc
# or 
yum install nmap-netcat

How to Redirect HTTP to HTTPs Zimbra 8.8.*

Zimbra Supports HTTPs by Default:

By default Zimbra will use HTTPs support only and disable HTTP use on the webmail client. But users will always use non http port to access the webclient. Users do not like to type https before the domain each time to get into the webmail client. Zimbra uses Nginx to run the proxy services to access the Javamail Client of Zimbra. Zimbra supports 5 types of proxy services through Nginx:

  1. redirect
  2. both
  3. http
  4. https
  5. mixed

You may check the following for details:

Enabling_Zimbra_Proxy_and_memcached

How to Redirect HTTP to HTTPs automatically in Zimbra 8.8*

The most popular out of 5 options for proxy services, is to redirect. To do this, you can run the following:

zmprov ms `zmhostname` zimbraReverseProxyMailMode redirect

This will redirect your URLs to the zimbra hostname based HTTPs.

Now, restart the proxy services:

su - zimbra
zmproxyctl restart

Hope this helps.

Odoo Controller JSON Route Returns 404 – werkzeug.exceptions.NotFound

Even though, if you have defined your routes properly, you are seeing an error of the following:

{
    "id": null,
    "jsonrpc": "2.0",
    "error": {
        "http_status": 404,
        "code": 404,
        "data": {
            "name": "werkzeug.exceptions.NotFound",
            "debug": "Traceback (most recent call last):\n  File \"/opt/odoo/odoo12/odoo/http.py\", line 656, in _handle_exception\n    return super(JsonRequest, self)._handle_exception(exception)\n  File \"/opt/odoo/odoo12/odoo/http.py\", line 314, in _handle_exception\n    raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])\n  File \"/opt/odoo/odoo12/odoo/tools/pycompat.py\", line 87, in reraise\n    raise value\n  File \"/opt/odoo/odoo12/odoo/http.py\", line 1460, in _dispatch_nodb\n    func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()\n  File \"/opt/odoo/odoo12-venv/lib64/python3.5/site-packages/werkzeug/routing.py\", line 1563, in match\n    raise NotFound()\nwerkzeug.exceptions.NotFound: 404: Not Found\n",
            "message": "404: Not Found",
            "exception_type": "internal_error",
            "arguments": []
        },
        "message": "404: Not Found"
    }
}

This error doesn’t return when you use the http.route as type ‘http’ or default, which is still http, but returns when you use the type ‘json’. One of the cause why the error return is that, you have multiple Odoo databases and Odoo is failing to detect the usable database for the type json. For the type, http, Odoo usually can predict what to use, while for the type json, it can not. For such cases, you would need to use the ‘db-filter’ to add the default database to load for Odoo on the odoo-bin command. If you are using the systemd service file, append the line with the following:

--db-filter=^my_prod$

where ‘my_prod’ is your database name.

So the service ExecStart would look like the following:

ExecStart=/usr/bin/scl enable rh-python35 -- /opt/odoo/odoo12-venv/bin/python3 /opt/odoo/odoo12/odoo-bin -c /etc/odoo.conf --limit-time-real 1009999999 --limit-time-cpu 1009999999 --limit-memory-hard 89179869184000000 --limit-memory-soft 57179869184000000 --db-filter=^my_prod$

After making the change, reload your systemctl and restart your odoo-bin:

systemctl daemon-reload
service odoo12 restart

This should do the job.

Grep Non ASCII Character Sets

I had an interesting challenge today about filtering a list using grep with a set like the following:

senegalese footballer|সেনেগালীয় ফুটবলার
species of insect|কীটপতঙ্গের প্রজাতি
indian cricket player|ভারতীয় ক্রিকেটার
Ajit Manohar Pai|অজিত পৈ|অজিত মনোহর পাই|অজিত ভরদ্বাজ পাই
ajit pai|অজিত পাই

You would need to grep to match them based on pipe. My target was to match lines that had multiple pipes, at least 2. I took a bit greedy approach for this to understand and find how to match Bengali characters using Grep. So, I started matching Alphanumeric first with a Pipe and then Bengali Characters with a Pipe, instead of just counting how many pipes I have at least.

If you are aware, conventional regex can detect and match unicode character sets like, if you want to match a ‘Greek’ set, you can do \p{Greek} in regular expressions. But for some reason, this wasn’t matching the Bengali in the following grep:

grep -Ei "\p{Bengali}" test.txt

I then looked at the grep manual and found a key information. Grep by default uses POSIX regex, and -E is just the extended version of POSIX grep. Unfortunately, this regex engine does not support PCRE, which is basically used to grep the unicode sets here. POSIX can only work with the HEX boundaries, which may sometimes get pretty difficult to match range boundaries of non ascii characters. To make it simpler, you can use PERL Regex that is a PCRE supporting engine. To use that, you may do the following:

grep -Pi "\p{Bengali}" test.txt

To get all the unicode that are available with a set in a PCRE supported Regex engine, you may check the following:

Regex Unicode Scripts

Now, let’s come to the original matching, what we have to match at least 2 pipes, the first one being the basic alphanumeric with whitespace being the simpler one:

grep -Pi "^[A-Za-z0-9\s]+\|" test.txt

Then, we need to add the First Bengali part with whitespace and a pipe

grep -Pi "^[A-Za-z0-9\s]+\|[\p{Bengali}\s]+\|" test.txt

This should suffice our purpose here in matching first being alphanumeric with a pipe, and second being the Bengali unicode set with a pipe at least.