Tuesday, February 17, 2015

Install 32/64bit Oracle instant client on 32/64bit Ubuntu







Before I continue the journey towards Docker, I have to digress a bit here. My “awesome” project has to use 32 bit Oracle instance client, and Docker can only run on 64bit. Installing 32bit Oracle instance client on 64bit Ubuntu turns out to be very very difficult. Of course, once you know something, it seems so simple. That is the curse of knowledge: if you know something, it is hard for you to understand why other people find it hard.

I wrote a python scrip to free my brain of having to remember various commands. The script does:

  • Check if Oracle instant client is already installed, if so, prompt user to choose whether to reinstall 
  •  Install 32bit Oracle instant client on 32bit Ubuntu
  •  Install 64bit Oracle instant client on 64bit Ubuntu
  •  Install 32bit Oracle instance client on 64bit Ubuntu
  •  If ora files directory is provided, copy tnsnames.ora, sqlnet.ora files into /usr/lib/oracle/11.2/{}/network/admin
  • Check if Oracle instance client has been successfully installed by checking if ldd sqlplus is successful.

This script won’t download Oracle instant client from Oracle site, you have to manually download.
·       To download 64bit Oracle instant client, go to http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html.

·       To download 32bit Oracle instant client, go to http://www.oracle.com/technetwork/topics/linuxsoft-082809.html.

You typically need to download three files: basic, devel, and sqlplus.
 
Here is how my directory structure looks like:



To run the script, first you need to copy the script and Oracle instance files into the destination machine, it is not possible to run the script in a shared folder.

After you run this successfully, you should see this output:

Note that you need to run source etc/profile to setup oracle environment variables in the shell. The script is not able to set the environment variable for the shell that it is run in.
This is because environment variables exist in a per-process memory space. When a new process is created with fork() it inherits its parent's environment variables. The child process can’t set environment variables for its parent process. 

If you want to set environment variables for the current process, for example, set environment variables inside the python process, here is a trick you can do:

envcmd = ['bash', '-c', 'source /etc/profile.d/oracleclient.sh && env']

proc = subprocess.Popen(envcmd, stdout = subprocess.PIPE)



for line in proc.stdout:

    (key, _, value) = line.partition("=")

    os.environ[key.strip()] = value.strip()

Below is the python script install_oracle_client.py:
#!/usr/bin/python 
import os
import sys
import subprocess
import glob
import re

class SystemSetup(object):


    def __init__(self):

        self.file_insert_header = (
            '\n################################################\n',
            '# author: Apple                    #\n',
            '################################################\n')

        self.rpm_files = {
            'basic': '',
            'devel': '',
            'sqlplus': ''        }

        self.clientbit64 = False
        self.clientfolder = ''        self.osbit64 = False

    def install_alien(self):
        print "**************install alien**************"        subprocess.check_call([ "apt-get", "-y", "install", "alien"])


    def install_libaio1(self):
        print "**************install libaio1**************"        subprocess.check_call(["apt-get", "-y", "install", "libaio1"])
        if self.osbit64 and not self.clientbit64:
            subprocess.check_call(["apt-get", "-y", "install", "libaio1:i386"])

    def find_file(self,fn,directory_containing_rpms):
        fnstr='{}/oracle*'+fn+'*rpm'        self.rpm_files[fn] = glob.glob(fnstr.format(directory_containing_rpms))
        if len(self.rpm_files[fn]) != 1:
            print "zero or more than one "+fn+" files exist: you should have only 1 oracle*"+fn+"*rpm file exists!"            print self.rpm_files[fn]
            exit (1)
        self.rpm_files[fn] = self.rpm_files[fn] [0]

        if self.clientbit64 and '64' not in self.rpm_files[fn]:
            print fn + " is not 64, while another file is 64"            exit(1)
        elif not self.clientbit64 and '64' in self.rpm_files[fn]:
            self.clientbit64=True

    def install_file(self, fn,directory_containing_rpms):
        if self.osbit64 == self.clientbit64:
            subprocess.check_call(['alien','-iv',self.rpm_files[fn]])
        elif self.osbit64 and not self.clientbit64:  #install32 client on 64bit ubuntu            #http://psgplacement.blogspot.com/2013/09/installing-i386-32-bit-rpm-files-in-amd.html            subprocess.check_call(['alien', '--to-tgz', '-v', self.rpm_files[fn]])
            tgzfile=glob.glob('*'+fn+'*.tgz'.format(directory_containing_rpms))
            if not tgzfile:
                print "can't find the generated "+fn+" tgz file, something must go wrong!"                exit(1)
            subprocess.check_call(['alien', '--to-deb', '-v', tgzfile[0]])

            debfile=glob.glob('*'+fn+'*.deb'.format(directory_containing_rpms))
            if not debfile:
                print "can't find the generated "+fn+" deb file, something must go wrong!"                exit(1)
            subprocess.check_call(['dpkg', '-i', debfile[0]])


    def oracle_setup(self, directory_containing_rpms, directory_containing_ora):
        self.find_file('basic',directory_containing_rpms)
        self.find_file('devel',directory_containing_rpms)
        self.find_file('sqlplus',directory_containing_rpms)

        if self.clientbit64:
            self.clientfolder="client64"        else:
            self.clientfolder="client"
        osbit=subprocess.check_output(["/bin/uname", "-m"])
        if 'x86_64' in osbit:
            self.osbit64 = True

        if self.osbit64 and not self.clientbit64:
            print "************** install i386 libraries**************"            subprocess.check_call([ 'dpkg','--add-architecture', 'i386'])
            subprocess.check_call(['apt-get','update'])
            subprocess.check_call([ 'apt-get','install', '-y','libc6:i386', 'libncurses5:i386','libstdc++6:i386'])

        self.install_alien()
        self.install_libaio1()

        print "**************install "+ self.rpm_files['basic'] +"**************"        self.install_file('basic',directory_containing_rpms)

        print "************** install "+ self.rpm_files['devel'] +"**************"        self.install_file('devel',directory_containing_rpms)

        print "************** install "+ self.rpm_files['sqlplus'] +"**************"        self.install_file('sqlplus',directory_containing_rpms)

        print "************** setup /etc/ld.so.conf.d/oracleclient.conf **************"        oracle_configuration_file = open('/etc/ld.so.conf.d/oracleclient.conf', 'w')
        oracle_configuration_file.writelines(self.file_insert_header)
        oracle_configuration_file.write("/usr/lib/oracle/11.2/{}/lib\n".format(self.clientfolder))
        oracle_configuration_file.close()

        subprocess.check_call(["ldconfig"])

        print "************** setup /etc/profile.d/oracleclient.sh **************"        system_environment_vars = open('/etc/profile.d/oracleclient.sh', 'w')
        system_environment_vars.writelines(self.file_insert_header)
        system_environment_vars.write(
                'export ORACLE_HOME=/usr/lib/oracle/11.2/{}\n'.format(self.clientfolder))
        system_environment_vars.write(
            'export TNS_ADMIN=/usr/lib/oracle/11.2/{}/network/admin\n'.format(self.clientfolder))
        system_environment_vars.write(
            'export LD_LIBRARY_PATH=/usr/lib/oracle/11.2/{}/lib\n'.format(self.clientfolder))
        system_environment_vars.close()
        subprocess.check_call(["chmod", "+x", "/etc/profile.d/oracleclient.sh"])

        #this actually doesn't work, /etc/profile is run inside the bash process, the environment variables are not set on the process that runs the python        # subprocess.Popen("source /etc/profile",shell=True, executable="/bin/bash")

        print "************** create "+"/usr/lib/oracle/11.2/{}/network/admin".format(self.clientfolder)+" **************"        if not os.path.exists("/usr/lib/oracle/11.2/{}/network/admin".format(self.clientfolder)):
            os.makedirs('/usr/lib/oracle/11.2/{}/network/admin'.format(self.clientfolder))

        if directory_containing_ora:
            #check_call will not glob file names, http://stackoverflow.com/questions/14482135/shell-expansion-in-python-subprocess            #subprocess.check_call(["cp", directory_containing_ora+"/*", '/usr/lib/oracle/11.2/{}/network/admin'.format(self.clientfolder) ])            cpCmd="cp "+directory_containing_ora + '/*.ora ' + '/usr/lib/oracle/11.2/{}/network/admin'.format(self.clientfolder)
            subprocess.call(cpCmd, shell=True)
        else:
            print "you need to obtain sqlnet.ora, tnsnames.ora, and possibly ldap.ora from your DBA"            print "and place these files into " +'/usr/lib/oracle/11.2/{}/network/admin'.format(self.clientfolder)

    def pre_check(self):
        sqlplus=subprocess.check_output(["whereis", "sqlplus"])
        m=re.search(r'sqlplus:(.*)',sqlplus)
        if not m or not m.group(1):
            return True

        where=m.group(1).strip().split(" ")[0]

        try:
            lddret=subprocess.check_output(["ldd", where])
            m=re.findall(r'(.*) => not found$',lddret,re.M)
            if m:
                for notFound in m:
                    # i18n can be ignored                    if not "i18n" in notFound:
                        print "sqlplus is installed, but its dependency "+notFound +" is not found, something might be wrong with the installation!"                        print "would you like to reinstall? press y to reinstall"                        userput=raw_input()
                        if userput== 'Y' or userput == 'y':
                            return "install"            else:
                print "sqlplus is installed, and it appears to be ok"                print "would you like to reinstall? press y to reinstall"                userput=raw_input()
                if userput== 'Y' or userput == 'y':
                    return True
        except:
            print where+"exists, but ldd "+where+" failed, will try to reinstall sqlplus"            return True

        return False

    def post_check(self):
        sqlplus=subprocess.check_output(["whereis", "sqlplus"])
        m=re.search(r'sqlplus:(.*)',sqlplus)
        if not m or not m.group(1):
            print "can't find sqlplus, , something might be wrong with the installation!"            exit(1)

        where=m.group(1).strip().split(" ")[0]

        lddret=subprocess.check_output(["ldd", where])
        m=re.findall(r'(.*) => not found$',lddret,re.M)
        if m:
            for notFound in m:
                # i18n can be ignored                if not "i18n" in notFound:
                    print notFound +" is not found, something might be wrong with the installation!"                    exit(1)

    def clean_up(self):
        subprocess.call("rm *.deb", shell=True)
        subprocess.call("rm *.tgz", shell=True)



if __name__ == '__main__':
    setup = SystemSetup()

    if setup.pre_check():
        if len(sys.argv) > 1:
            setup.oracle_setup(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None)
        else:
            setup.oracle_setup(".", None)

    setup.post_check()
    setup.clean_up()

    print "Note, you will need to run source /etc/profile to make the environment variables defined in /etc/profile.d/oracleclient.sh to take effect!"







1 comment:

  1. Thank you for sharing, I find this article is very helpful and easy to understand. Keep up the good work, guys!


    Web Hosting Services

    ReplyDelete