How to Fix Locale Font Issue with Odoo Qweb Reports

Issue

When looking at the html report in Odoo, locale fonts look ok, but if you download the Qweb report to print in pdf format, it prints gibberish. How to fix that?

Resolution

Odoo uses a templating engine for reporting called ‘Qweb’. Qweb can be used to generate two types of reports. One is HTML and the other is PDF. Odoo primarily uses Qweb engine to generate the HTML code. After that, it uses a tool called ‘wkhtmltopdf’ to convert the report to pdf and make it printable. Now when, we look at the HTML version of the report, fonts are shown based on Unicode supported browsers or the fonts you have installed on your computer. But when you try to convert this to PDF using wkhtmltopdf, that tool has to have exclusive access to those fonts to be able to convert them from HTML to pdf for you. As wkhtmlpdf command runs in the server you have installed Odoo, hence, you would need to install the font package in the server.

In my case, I required to install Bengali fonts. For CentOS, it is available under the lohit package, that contains several indian fonts including bengali. To install bengali font package in CentOS 7, use the following command:

yum install lohit-bengali* -y

Once done, your wkhtmltopdf should be able to read the bengali fonts from your html/qweb templates and able to convert them to PDF for you.

How to Add fields to res.users or res.partners model in Odoo

res.users and res.partners tables are two base tables of odoo. If you would like to inherit and extend them, remember, you can’t do it from the Odoo user view. The reason is, when you do an upgrade from the user view, it has to be something that works over base module, not base module itself. Hence, you will get a 500 error or internal server error when trying to upgrade the module.

We will make a simple module for res.users to extend the model to add a field called ‘access_token’ for each user, and generate a key automatically when a user is added.

I will only post the model file and the view file here. I expect you already know how to write an Odoo module.

This is my res_users.py file

from odoo import api, fields, models
import string, random

class res_users_ex(models.Model):
    _inherit = 'res.users'

    access_token = fields.Text(string='API Access Token', default=False)

    def create(self, vals):
        key = ''.join(random.choices(string.ascii_lowercase + string.digits, k = 48))
        user_id = super(res_users_ex, self).create(vals)
        if key:
            user_id.access_token = key

        return user_id

Here is the xml view file:

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <record model="ir.ui.view" id="view_res_users_access_token">
        <field name="name">res.users.add.access.token</field>
        <field name="model">res.users</field>
        <field name="inherit_id" ref="base.view_users_form"/>
        <field name="type">form</field>
        <field name="arch" type="xml">
        <xpath expr="//notebook" position="inside">
            <page string="API Details">
               <group col="4">
                  <field name="access_token"/>
                </group>
             </page>
        </xpath>
        </field>
    </record>
</odoo>

Now, after you have added this to a module, you can not simply upgrade this from App >> Module >> Upgrade. You need to upgrade this module via command line like the following:

First switch to your odoo user, in my case, it is ‘odoo’

su - odoo

Now, first stop your current odoo with the following:

service odoo14 stop

Once done, now you can upgrade the module with the following:

/usr/bin/scl enable rh-python36 -- /opt/odoo/odoo14-venv/bin/python3 /opt/odoo/odoo14/odoo-bin -d my_database-u res_users_access_token

The command above is explained like the following:

python3 odoo-bin -d your_databasename -u module_name

In my case, I use virtual environment and scl for python, hence the python3 source is like the following:

/usr/bin/scl enable rh-python36 -- /opt/odoo/odoo14-venv/bin/python3

The next one is the binary of odoo with it’s location, which should be odoo-bin. With the parameter -d, you give your database name, and with the parameter -u, you need to give your module name. After you run the command, you should see no ‘Error’ or Red marked line in your console. If not, it shall be upgraded. Now do control + c, and start your odoo again to see the new fields being visible in your Users tab.

Why, when and how to use Context Manager in Python?

Context Manager in Python

In one line, context managers are an efficient way of handling resources in Python. So, what kind of resources are they? It could be any logical resource that you are using for your software, a common one, is database connections, or the files or in few cases, locks for concurrency control.

How exactly Context Manager is efficient?

If we talk about efficient programs, there could be several meanings of it in computer science. For our case, we mean efficient by writing less code, or more specifically, not writing repetitive codes for managing resources. There are some pinpoint benefits of using less repetitive codes other than writing more codes, which is purely technical. You do not forget to perform a step, that is essential if you do it from one source. Let’s find an example. You have a code, that connects to a remote FTP and uploads some content, once done, it closes the connection. If you do the process in several places, you might miss out to close the FTP connection in a place, that is accessed several times by the users. If that is so you might run out of the FTP connection pool on a random day. It is essential to close the connection after you are done with the connection to free the resources. Context Managers can help you write a code, that does the job for you, without the need to remember closing the connection each time.

The ‘with’ statement in Python

Before we can go deeper with Context Managers, we need to learn something about ‘with’ statement in python. ‘with’ is a special statement in python, that does jobs automatically for you. One, it calls a method of setting class, that is called ‘__enter__’ when it calls it, and the other, it automatically calls ‘__exit__’ method when it completes running its code. Let’s do some coding now

with open('test.txt') as f:
    f.write("testing")

In the above code, we are opening a file using the ‘with’ statement of python, then writing some texts in it. But wait, we haven’t closed the file, did you notice? Isn’t it necessary to close a file after opening it to free up the system file descriptors? Absolutely, but using the ‘with’ statement, do it for you even if you don’t do it in your code. In Python, the open() method for opening a file, can be used as Context Manager. For Context Manager, two methods are essential, one is when you set up the call, which is ‘__enter__’ and the other is, when you end the code, that is ‘__exit__’. ‘with’ statement in python is created to be used for Context Managers. As I said, the open() method can be used as Context Manager, which means, Python has both of these methods defined by default with the open() method, and can be used using the ‘with’ statement.

Why and when do we need Context Managers?

Before jumping into, how can we do context managers, let’s understand, if we understand the need of context managers properly with an example! The primary purpose of a context manager is to write cleaner nonrepetitive codes. Do we need this often? Yes, we do. A common example would be in setting up your database connections. If you are setting up a database connection and clean up the things once done, you may create a context manager to do that. There are more complex database use cases of context managers. Let’s focus on one of them. Let’s say, you would like to utilize the database ‘SAVEPOINT’ in a cleaner way, manage the release, and rollback for concurrent transactions based on the savepoint you create dynamically, a clean technique would be to use context manager. Pseudocode could be like the following for this kind of context manager

FUNCTION SAVEPOINT()
    NAME = UUID()
    SQL.EXECUTE('SAVEPOINT ' + UUID)
    TRY
        YIELD
    EXCEPTION
        EXECUTE('ROLLBACK TO SAVEPOINT ' + UUID)
        RAISE
    FINALLY
        EXECUTE('RELEASE SAVEPOINT ' + UUID)

What this context manager is trying to do, is generating a savepoint with a name for you. Once done, it yields the code you instruct it to run after the ‘with’ statement. If you create an exception from those codes, it rollback and sends the exception to the main program else if not it releases the savepoint and gives control to the code after the ‘with’ statement. This is technically the most efficient way of using Savepoint for SQL in your code. Similarly so, we can acquire and release locks for concurrent control using Context managers, or processing an API that had a setup call and an end/cleanup call.

How can we write Context Manager in Python?

There are two ways you can do it. One is using Python class, and the other use, using contextlib and the contextmanager decorator. Let’s first check out, how to do it using Python class to understand the concept better.

First, we want to emulate the way Python uses ‘open’ method as context manager using our own context manager class. A context manager class that can be used using ‘with’ statement could be like the following:

class Open_A_File():
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        self.f = open(self.name)
        return self.f
    
    def __exit__(self, exc_type, exc_val, traceback):
        self.f.close()

with Open_A_File('test.txt') as f:
    f.write('Class Test')

In our class ‘Open_A_File()’, we have 3 methods. Our constructor __init__ method and other two methods are __enter__ and __exit__. When we used Open_A_File() using ‘with’ statement with a parameter, it setup our filename variable for the class using constructor, and then, calls the __enter__ method. It then opens the file and returns the file object. When it returns the file object, we catch it as ‘f’ to use in our code under the ‘with’ statement. We then write the code and the codes within ‘with’ statement ends, hence the __exit__ is automatically called, that closes the file object by calling ‘close()’. We can technically convert any class into a Context Manager and use them using ‘with’ statement if can define the methods to do while entering and exiting the class when called directly with the ‘with’ statement.

Other than class, we can use Context Managers using a function, through the use of contextlib library. This is the most used method of using Context Managers. We used the idea of this, in our pseudocode while demonstrating earlier. Let’s rewrite the above code using contextlib below

from contextlib import contextmanager

@contextmanager
def open_a_fiie(name):
    try:
        f = open(file)
        yield f
    finally:
        f.close()

with Open_A_File('test.txt') as f:
    f.write('Contextlib Test')

We first import contextmanager decorator from contextlib and then, we define a normal function. Although, there exists a ‘yield’ statement. For context managers, the statements that exist before yield would execute on __enter__ method, and the statements after yield would execute on __exit__ method. If you want to return anything to the ‘with’ call, then you need to specify that after yield, as we did in yield f, that means, we returned the file object to the ‘wite’ statement. the yield would replace the code, we run after the ‘with’ statement, like the f.write() in our case.

Hope this make sense. For confusion, or in case you would like to add some, do comment below. Thanks for reading.

How to find wifi password from saved wifi connection in Windows 10

You may get the password from using ‘netsh’ windows command. First go to your windows 10 search box, and type ‘cmd’

Now, from the result, there should be an option called ‘Command Prompt’, right click on it, and ‘Run as administrator’. Now on the black command prompt, type the following:

netsh wlan show profile "your wifi name" key=clear

Replace the part “your wifi name” with your one. So, for example if you connect to a wifi connection that has a name ‘Mellowhost_Portable’, then the command should be like the following:

netsh wlan show profile "Mellowhost_Portable" key=clear

This shall show you the full profile of your wifi details, including the password. Password should be available under the ‘Security Settings’, inside the ‘Key Content’ section.

Hope it helps.

Odoo Error – virtual real time limit (151/120s) reached.

There are times, when you might suddenly see your Odoo is shutdown automatically, without warning. Once you enable to logging, you could see an error like the following:

virtual real time limit (151/120s) reached.

or in full details like the following

2021-04-22 06:46:44,054 32685 WARNING ? odoo.service.server: Thread <Thread(odoo.service.http.request.140015617943296, started 140015617943296)> virtual real time limit (151/120s) reached.
2021-04-22 06:46:44,054 32685 INFO ? odoo.service.server: Dumping stacktrace of limit exceeding threads before reloading
2021-04-22 06:46:44,060 32685 INFO ? odoo.tools.misc:
# Thread: <Thread(odoo.service.http.request.140015617943296, started 140015617943296)> (db:n/a) (uid:n/a) (url:n/a)
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/threading.py", line 884, in _bootstrap
  self._bootstrap_inner()
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/threading.py", line 916, in _bootstrap_inner
  self.run()
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/threading.py", line 864, in run
  self._target(*self._args, **self._kwargs)
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/socketserver.py", line 654, in process_request_thread
  self.finish_request(request, client_address)
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/socketserver.py", line 364, in finish_request
  self.RequestHandlerClass(request, client_address, self)
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/socketserver.py", line 724, in __init__
  self.handle()
File: "/opt/odoo/odoo14-venv/lib64/python3.6/site-packages/werkzeug/serving.py", line 329, in handle
  rv = BaseHTTPRequestHandler.handle(self)
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/http/server.py", line 418, in handle
  self.handle_one_request()
File: "/opt/odoo/odoo14-venv/lib64/python3.6/site-packages/werkzeug/serving.py", line 360, in handle_one_request
  self.raw_requestline = self.rfile.readline()
File: "/opt/rh/rh-python36/root/usr/lib64/python3.6/socket.py", line 586, in readinto
  return self._sock.recv_into(b)
2021-04-22 06:46:44,060 32685 INFO ? odoo.service.server: Initiating server reload

This is because Odoo is killing zombie processes and probably mistakenly crashing your Odoo completely while doing so. The parameter that is used for this purpose, can be found in Odoo documentation:

https://www.odoo.com/documentation/14.0/reference/cmdline.html

--limit-time-real <limit>
Prevents the worker from taking longer than <limit> seconds to process a request. If the limit is exceeded, the worker is killed.

Differs from --limit-time-cpu in that this is a “wall time” limit including e.g. SQL queries.

Defaults to 120.

You may start your service command with something like –limit-time-real 100000 to avoid Odoo from auto killing processes. A command could look like the following if you edit your service file located at:

/etc/systemd/system/odoo14.service

The exec would be like the following:

ExecStart=/usr/bin/scl enable rh-python36 -- /opt/odoo/odoo14-venv/bin/python3 /opt/odoo/odoo14/odoo-bin -c /etc/odoo.conf --limit-time-real=100000

Once the change is done, save the file, and reload the system daemon and restart your Odoo

systemctl daemon-reload
service odoo14 restart

How to manually install/renew Let’s Encrypt SSL in Zimbra

If you are having trouble installing Let’s Encrypt SSL with the certbot-zimbra.sh file, then probably you would need to follow this tutorial. To follow this tutorial, we first need to install certbot. certbot has a built in web server to allow you get the certificate without actually installing an extra web server or through Zimbra web server (nginx to be specific).

First, we install certbot with the following:

// install epel-release first
yum install epel-release
// install certbot from epel
yum install certbot

Once done, you may now use the following command to ensure certbot is working:

# certbot --help

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...

Certbot can obtain and install HTTPS/TLS/SSL certificates.  By default,
it will attempt to use a webserver both for obtaining and installing the
certificate. The most common SUBCOMMANDS and flags are:

obtain, install, and renew certificates:
    (default) run   Obtain & install a certificate in your current webserver
    certonly        Obtain or renew a certificate, but do not install it
    renew           Renew all previously obtained certificates that are near
expiry
    enhance         Add security enhancements to your existing configuration
   -d DOMAINS       Comma-separated list of domains to obtain a certificate for

  (the certbot apache plugin is not installed)
  --standalone      Run a standalone webserver for authentication
  --nginx           Use the Nginx plugin for authentication & installation
  --webroot         Place files in a server's webroot folder for authentication
  --manual          Obtain certificates interactively, or using shell script
hooks

   -n               Run non-interactively
  --test-cert       Obtain a test certificate from a staging server
  --dry-run         Test "renew" or "certonly" without saving any certificates
to disk

manage certificates:
    certificates    Display information about certificates you have from Certbot
    revoke          Revoke a certificate (supply --cert-name or --cert-path)
    delete          Delete a certificate (supply --cert-name)

manage your account:
    register        Create an ACME account
    unregister      Deactivate an ACME account
    update_account  Update an ACME account
  --agree-tos       Agree to the ACME server's Subscriber Agreement
   -m EMAIL         Email address for important account notifications

More detailed help:

  -h, --help [TOPIC]    print this message, or detailed help on a topic;
                        the available TOPICS are:

   all, automation, commands, paths, security, testing, or any of the
   subcommands or plugins (certonly, renew, install, register, nginx,
   apache, standalone, webroot, etc.)
  -h all                print a detailed help page including all topics
  --version             print the version number
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Once you ensure certbot is installed, now you may use certbot to get the certificate, using the certbot –standalone tag. Remember to stop zimbra first, as Zimbra also runs a nginx web server, that would prevent certbot to use standalone or it’s own web server to verify certificate.

// from root, run
[root@mailapp ~]# service zimbra stop

// wait until zimbra stops, once done, use the following to get certificate for your domain/hostname in place of mail.domain.com
[root@mailapp ~]# certbot certonly --standalone -d mail.domain.com

This would get your certificate and save it in:

/etc/letsencrypt/live/mail.domain.com

Now, that folder would contain 4 files. Something like the following:

]# ls -la /etc/letsencrypt/live/mail.domain.com/
total 16
drwxr-xr-x 2 root root 4096 Apr 16 11:30 .
drwx------ 4 root root 4096 Feb 10  2020 ..
lrwxrwxrwx 1 root root   40 Apr 16 11:30 cert.pem -> ../../archive/mail.domain.com/cert8.pem
lrwxrwxrwx 1 root root   41 Apr 16 11:30 chain.pem -> ../../archive/mail.domain.com/chain8.pem
lrwxrwxrwx 1 root root   45 Apr 16 11:30 fullchain.pem -> ../../archive/mail.domain.com/fullchain8.pem
lrwxrwxrwx 1 root root   43 Apr 16 11:30 privkey.pem -> ../../archive/mail.domain.com/privkey8.pem

As you can see, these files are symbolically linked to another files, depends on how many time you are running certbot. Each time, it generates a number liker cert8.pem, the next one would be cert9.pem and so on. So the orignal files are here:

/etc/letsencrypt/archive/mail.domain.com/cert8.pem
/etc/letsencrypt/archive/mail.domain.com/chain8.pem
/etc/letsencrypt/archive/mail.domain.com/fullchain8.pem
/etc/letsencrypt/archive/mail.domain.com/privkey8.pem

Now, we have our certificates. We need to follow a couple of steps to make sure everything is set correctly.

First, zimbra SSL files are stored here

/etc/zimbra/ssl/letsencrypt

We clean all old pem files

rm -f /etc/zimbra/ssl/letsencrypt/*

Now, copy the pem files we got to this folder with the following:

cp /etc/letsencrypt/archive/mail.domain.com/cert8.pem /opt/zimbra/ssl/letsencrypt/cert.pem
cp /etc/letsencrypt/archive/mail.domain.com/chain8.pem /opt/zimbra/ssl/letsencrypt/chain.pem
cp /etc/letsencrypt/archive/mail.domain.com/fullchain8.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem
cp /etc/letsencrypt/archive/mail.domain.com/privkey8.pem /opt/zimbra/ssl/letsencrypt/privkey.pem

Check, how we are renaming all the files with number to file name without number, like cert8.pem is moved as cert.pem here.

Now, change the ownership of these files to zimbra with the following:

chown -Rf zimbra:zimbra /opt/zimbra/ssl/letsencrypt/*

Now, we are done from root, change your ownership to zimbra

su - zimbra

First job, is to change your directory to the ‘/opt/zimbra/ssl/letsencrypt/’

cd /opt/zimbra/ssl/letsencrypt/

Let’s Encrypt files are very much ready to use, only with one problem. Let’s Encrypt do not add it’s root CA certificate with it’s chain.pem file. We need to do this. First open the certificate with nano editor as following:

nano chain.pem

Now, at the end of the file, add the following section:

-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

After adding the above, your chain.pem file should look like the following

-----BEGIN CERTIFICATE-----
your chain pem encrypted certificate here
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

Now, save the file (CTRL + o) and exit (CTRL + x)

We need to do one more thing before we are ready to verify and deploy the certificate. We need to set the letencrypt private key that we used to generate the certificate as commercial.key of zimbra. You may do this with the following two commands:

rm -f /opt/zimbra/ssl/zimbra/commercial/commercial.key
cp /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key

Now, you are ready to complete the job. First verify if everything is alright with the following:

[zimbra@mailapp letsencrypt]$ /opt/zimbra/bin/zmcertmgr verifycrt comm privkey.pem cert.pem chain.pem
** Verifying 'cert.pem' against 'privkey.pem'
Certificate 'cert.pem' and private key 'privkey.pem' match.
** Verifying 'cert.pem' against 'chain.pem'
Valid certificate chain: cert.pem: OK

If everything is ok, you may now deploy certificate with the following command:

/opt/zimbra/bin/zmcertmgr deploycrt comm cert.pem chain.pem

Once the certificate is deployed successfully, get out from the zimbra user to root user with the following command

exit

Now, you may start/restart zimbra with the following command:

service zimbra restart

If everything went right, you should now be able to go to your zimbra domain, and under the lock sign on the left of the domain shown in browser, you may click on it to see the extended date of ssl expiry. Sweet!

How to Fix – Rongta 80mm Thermal Printer – Firefox – ‘an unknown error occurred while printing’

Rongta 80mm Thermal printer is the most popular POS receipt printer in the world. It can run on a regular 80mm printer driver for windows. It is possible to print receipts from a web browser like firefox or chrome to Rongta printers. Although there are times, it might become hectic with different printing issues to send the printing data to the printer from the browser.

If you are trying to print your POS receipt from Firefox for a Rongta printer, a common problem, that appears time to time, is the following:

An unknown error occurred while printing

You probably failed to resolve the issue, even after uninstalling the Firefox or by Refreshing Firefox settings, or by clearing the printer saved settings on your browser. The issue is very weird. For some reason, Javascript’s window print fails to send the data to Printer through firefox, even though it thinks it did. I tried digging several things, but nothing worked until I found a way to actually make firefox think, it’s a new printer. Here are the steps you may following.

  1. Close the firefox, and make your Rongta 80mm printer the default printer of your system.
  2. Now open firefox, and type ‘about:support’ & enter.
  3. It will open a page. Scroll down to the printing section where it says Troubleshooting with ‘Clear saved print settings’. Click on the button to clear the settings.
  4. From the top press ‘Refresh Firefox’ button.
  5. Now restart your firefox, and type ‘about:config’. These is the firefox configurable environmental variables, that we wish to change some.
  6. Before we do, we first need to make firefox save, Rongta settings. One quick way of doing this is to open a new tab, browse google.com, now go to Print from the File tab, it will show you ‘Advanced Preference’, now from the Print button at the top, make sure Rongta 80mm printer is selected and press Print. It will probably not print anything, but what it will help to do, is creating our printer settings in firefox.
  7. Now go back to the tab with ‘about:config’, at the top, type ‘print’ without quotes in the box that says ‘Search for preference’
  8. A lot of settings should appear, a couple of things you need to change.
  9. Delete print_printer or print.print_printer row.
  10. Set print.print_via_parent boolean value to ‘true’.
  11. Set print.tab_modal.enabled to ‘false’
  12. Make sure dom.enable_window_print this is set to ‘true’
  13. Make sure print.show_print_progress is set to ‘true’
  14. Now, close the window, and restart your firefox again.

You should now be able to print your POS receipt using Rongta 80mm printer using Firefox.

How to Setup Odoo 14 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 14 had come out already. I will have a straight forward how to on installing the latest Odoo 14 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 14. Odoo 13 also ran on Python 3.6. 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 10 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 10, we would use PostgreSQL official repository to download and install the 10 version.

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

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

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

yum install postgresql10 postgresql10-server postgresql10-contrib postgresql10-libs -y

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

# Initialize the DB
/usr/pgsql-10/bin/postgresql-10-setup initdb

# Start the database
systemctl start postgresql-10.service

If everything goes alright, now you may check the postgresql 10 status:

[root@docker bin]# systemctl status postgresql-10.service
● postgresql-10.service - PostgreSQL 10 database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql-10.service; disabled; vendor preset: disabled)
   Active: active (running) since Mon 2021-04-05 16:42:15 EDT; 5s ago
     Docs: https://www.postgresql.org/docs/10/static/
  Process: 6380 ExecStartPre=/usr/pgsql-10/bin/postgresql-10-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
 Main PID: 6386 (postmaster)
    Tasks: 8
   Memory: 13.5M
   CGroup: /system.slice/postgresql-10.service
           ├─6386 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/data/
           ├─6388 postgres: logger process
           ├─6390 postgres: checkpointer process
           ├─6391 postgres: writer process
           ├─6392 postgres: wal writer process
           ├─6393 postgres: autovacuum launcher process
           ├─6394 postgres: stats collector process
           └─6395 postgres: bgworker: logical replication launcher

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

systemctl enable postgresql-10.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 14

If you have come all through here, that means you are done with the all dependency installations before starting to download Odoo 14 source code. We will download Odoo 14 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 14.0 /opt/odoo/odoo14

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

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 odoo14-venv

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

source odoo14-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 odoo14/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/odoo14-custom-addons
chown odoo: /opt/odoo/odoo14-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/odoo14/addons,/opt/odoo/odoo14-custom-addons
; You can enable log file with uncommenting the next line
; logfile = /var/log/odoo14/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 14

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/odoo14.service
[Unit]
Description=Odoo14
Requires=postgresql-10.service
After=network.target postgresql-10.service
[Service]
Type=simple
SyslogIdentifier=odoo14
PermissionsStartOnly=true
User=odoo
Group=odoo
ExecStart=/usr/bin/scl enable rh-python36 -- /opt/odoo/odoo14-venv/bin/python3 /opt/odoo/odoo14/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 14 instance using the following command:

systemctl start odoo14

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

systemctl status odoo14

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 odoo14

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

journalctl -u odoo14

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

logfile = /var/log/odoo14/odoo.log

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

Test the Installation


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 youCentOS Linux release 7.8.2003 (Core)

Now, you may try updating the system with yumyum 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/odoouseradd -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 databasesystemctl 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.rpmyum 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/odoopython3 -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 pippip3 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 userdeactivate && 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-addonschown 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_databasedb_host = Falsedb_port = Falsedb_user = odoodb_password = Falseaddons_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=Odoo13Requires=postgresql-9.6.serviceAfter=network.target postgresql-9.6.service[Service]Type=simpleSyslogIdentifier=odoo13PermissionsStartOnly=trueUser=odooGroup=odooExecStart=/usr/bin/scl enable rh-python35 — /opt/odoo/odoo13-venv/bin/python3 /opt/odoo/odoo13/odoo-bin -c /etc/odoo.confStandardOutput=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.conflogfile = /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 Speed Up Software RAID (mdadm) Resync Speed

mdadm is the software raid tools used in Linux system. One key problem with the software raid, is that it resync is utterly slow comparing with the existing drive speed (SSD or NVMe). The resync speed set by mdadm is default for regardless of whatever the drive type you have. To view the default values, you may run the following:

[root@172 ~]# sysctl dev.raid.speed_limit_min
dev.raid.speed_limit_min = 1000
[root@172 ~]# sysctl dev.raid.speed_limit_max
dev.raid.speed_limit_max = 200000

As you see, the minimum value starts from 1000 and can max upto 200K. Although, it can max upto 200K, but as min value is too low, mdadm always tries to keep the value below average to your speed available. To speed up, we would like to maximize these numbers. To change the numbers, you may run something like the following:

sysctl -w dev.raid.speed_limit_min=500000
sysctl -w dev.raid.speed_limit_max=5000000

Once done, you may now check the speed is going up immediately:

[root@172 ~]# cat /proc/mdstat
Personalities : [raid1]
md3 : active raid1 sdb5[1] sda5[0]
      1916378112 blocks super 1.2 [2/2] [UU]
      [===============>.....]  resync = 76.2% (1461787904/1916378112) finish=27.4min speed=276182K/sec
      bitmap: 5/15 pages [20KB], 65536KB chunk

md0 : active raid1 sdb1[1] sda1[0]
      1046528 blocks super 1.2 [2/2] [UU]
      bitmap: 0/1 pages [0KB], 65536KB chunk

md2 : active raid1 sda2[0] sdb2[1]
      78576640 blocks super 1.2 [2/2] [UU]
      bitmap: 1/1 pages [4KB], 65536KB chunk

md1 : active raid1 sdb3[1] sda3[0]
      4189184 blocks super 1.2 [2/2] [UU]

unused devices: <none>

One thing to keep in mind is that, if you try to set the value too high, like we did, this might cause some handsome load on your system. If you see the load is unmanageable, you should focus on decreasing the number to something like 50k-100k for the min value.

Making The Sysctl Value Permanent

As we have established the kernel variable values on runtime, this would go back to default once we restart/reboot the server. If you want to persist the values, you need to put these values to /etc/sysctl.conf file. To make them persist, open the sysctl.conf file:

nano /etc/sysctl.conf

Add the following lines at the end of the file:

dev.raid.speed_limit_min = 500000
dev.raid.speed_limit_max = 5000000

Save the file, and run the following command:

sysctl -p

This should persist your values for those variables after the reboot as well as runtime.

-bash: smartctl: command not found

Error Details

When I try to check the S.M.A.R.T details of my drive, using the following command:

smartctl -a /dev/sda

I get an error:

-bash: smartctl: command not found

What can I do?

Solution

This error is appearing because you do not have the S.M.A.R.T tools installed on your system.

How to Install Smart tools on CentOS 7?

To install smart tools, you can run the following:

yum install smartmontools -y

Once done, you may run the smartctl command again, and it shall work:

[root@172 ~]# smartctl -a /dev/sda
smartctl 7.0 2018-12-30 r4883 [x86_64-linux-3.10.0-1127.el7.x86_64] (local build)
Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF INFORMATION SECTION ===
Model Family:     Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs
Device Model:     Micron_1100_MTFDDAK2T0TBN