Tuesday, November 21, 2017

CRS-2632: There are no more servers to try to place resource 'ora.qa2tesp.db' on that would satisfy its placement policy

Trying to start the RAC database after cloning from other RAC database and getting the error below:

[oracle@ract01n01 (qa2tesp1) dbs]$ srvctl start database -d qa2tesp
PRCR-1079 : Failed to start resource ora.qa2tesp.db
CRS-5017: The resource action "ora.qa2tesp.db start" encountered the following error:
ORA-01102: cannot mount database in EXCLUSIVE mode
. For details refer to "(:CLSN00107:)" in "/u01/app/grid/diag/crs/ract01n02/crs/trace/crsd_oraagent_oracle.trc".
CRS-2674: Start of 'ora.qa2tesp.db' on 'ract01n02' failed
CRS-2632: There are no more servers to try to place resource 'ora.qa2tesp.db' on that would satisfy its placement policy
[oracle@ract01n01 (qa2tesp1) dbs]$ 


After that error I have noticed that the database is running on n01 but not on n02

oracle@ract01n01 (qa2tesp1) dbs]$ srvctl status database -d qa2tesp
Instance qa2tesp1 is running on node ract01n01
Instance qa2tesp2 is not running on node ract01n02
[oracle@ract01n01 (qa2tesp1) dbs]$ 


That gave me a idea that some how there is an issue with the cluster_database parameter

SQL> show parameter cluster_dat
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cluster_database                     boolean     FALSE
cluster_database_instances           integer     1
SQL> 

but when I had a look at the init file which was used to create the spfile it says cluster_database=TRUE

That is interesting, it looks like the correct spfile is not in use. But looking at the $ORACLE_HOME/dbs directory it looks like I forgot to move the spfile from the directory after the restore and recovery

[oracle@ract01n01 (qa2tesp1) dbs]$ ls -lah *qa2tes*
-rw-r--r-- 1 oracle oinstall   63 Nov 21 09:40 initqa2tesp1.ora
-rw-r----- 1 oracle oinstall 2.5K Nov 17 14:31 orapwqa2tesp1
-rw-r----- 1 oracle asmadmin  14K Nov 21 09:40 spfileqa2tesp1.ora
[oracle@ract01n01 (qa2tesp1) dbs]$ 


Solution:

I have moved the spfile away from that folder and tried starting the database and it worked

[oracle@ract01n01 (qa2tesp1) dbs]$ mv spfileqa2tesp1.ora ./backup/
[oracle@ract01n01 (qa2tesp1) dbs]$
[oracle@ract01n01 (qa2tesp1) dbs]$ srvctl start database -d qa2tesp
[oracle@ract01n01 (qa2tesp1) dbs]$ 


Monday, November 13, 2017

ORA-19809: limit exceeded for recovery files

While trying to duplicate the database using RMAN we are encountering error below:


ORA-01547: warning: RECOVER succeeded but OPEN RESETLOGS would get error below
ORA-01152: file 1 was not restored from a sufficiently old backup
ORA-01110: data file 1: '+DATA/qa1tst/datafile/system.593.959955191'

released channel: c1
released channel: c2
released channel: c3
released channel: c4
released channel: c5
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 11/13/2017 14:24:49
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
ORA-19870: error while restoring backup piece 933347_PRD1DAD_i7sjseoh_1_1
ORA-19809: limit exceeded for recovery files
ORA-19804: cannot reclaim 65011712 bytes disk space from 128849018880 limit

by looking at the alert logs for the instance we noticed the error that db_recovery_file_dest_size is fully used.

ORA-19815: WARNING: db_recovery_file_dest_size of 128849018880 bytes is 100.00% used, and has 0 remaining bytes available.


That is the reason for this error.

We have increased the size in init parameter while doing recovery and recovery has worked this time.

Thursday, October 12, 2017

Warning: ORA-16809: multiple warnings detected for the database

Getting the error Warning: ORA-16809: multiple warnings detected for the database when using show configuration in dgmgrl

show database qa3tstp gave me the hit to check if the standby logs are created on primary or not.

Apparently they were not.

In short:

Stopped DR database
Created standby log on primary
Started DR database



Details:

DGMGRL> show configuration

Configuration - qa3tst_dg

  Protection Mode: MaxPerformance
  Members:
  qa3tstp - Primary database
    Warning: ORA-16809: multiple warnings detected for the database

    qa3tstd - Physical standby database
      Warning: ORA-16789: standby redo logs configured incorrectly

Fast-Start Failover: DISABLED

Configuration Status:
WARNING   (status updated 36 seconds ago)

DGMGRL>

DGMGRL> show database qa3tstp

Database - qa3tstp

  Role:               PRIMARY
  Intended State:     TRANSPORT-ON
  Instance(s):
    qa3tstp1
    qa3tstp2

  Database Warning(s):
    ORA-16789: standby redo logs configured incorrectly
    ORA-16789: standby redo logs configured incorrectly

Database Status:
WARNING

DGMGRL>



SQL> select group#, bytes/1024/1024 Log_file_size_MB from v$standby_log;

no rows selected

SQL>




[oracle@ractestdr01n01 (qa3tstd1) dbs]$ srvctl stop database -d qa3tstd




SQL> select group#, bytes/1024/1024 Log_file_size_MB from v$log;

    GROUP# LOG_FILE_SIZE_MB
---------- ----------------
         1              100
         2              100
         3              100
         4              100
         5              100
         6              100

6 rows selected.

SQL>
SQL>
SQL> alter database add standby logfile thread 1 size 104857600;
alter database add standby logfile thread 1 size 104857600;
alter database add standby logfile thread 1 size 104857600;

Database altered.

SQL>
Database altered.

SQL>
Database altered.

SQL> alter database add standby logfile thread 2 size 104857600;
alter database add standby logfile thread 2 size 104857600;
alter database add standby logfile thread 2 size 104857600;

Database altered.

SQL>
Database altered.

SQL>
Database altered.

SQL>
SQL>
SQL>



[oracle@ractestdr01n01 (qa3tstd1) dbs]$ srvctl start database -d qa3tstd




DGMGRL> connect sys
Password:
Connected as SYSDG.
DGMGRL> show configuration

Configuration - qa3tst_dg

  Protection Mode: MaxPerformance
  Members:
  qa3tstp - Primary database
    qa3tstd - Physical standby database

Fast-Start Failover: DISABLED

Configuration Status:
SUCCESS   (status updated 6 seconds ago)

DGMGRL>

In case the standby logs are no there on DR side then you need to create them as well.

Creating the standby logs on DR

Monday, October 9, 2017

Silly mistake

As we move more one version of database to another I have a habit of updating the database name with 1,2,3,4 ...

in this case
qa1tst => 11.2.0.3
qa2tst => 12.1.2.0.2

I tired adding the password file and got a very sensible message and quickly realised that I am entering the wrong unique name



[oracle@testdbn01 (qa2tstp1) dbs]$ srvctl modify database -d qa1tstp -pwfile "+DATA/QA2TSTP/PASSWORD/pwdqa2tstp.9085.127745674"
PRCD-1229 : An attempt to access configuration of database qa1tstp was rejected because its version 11.2.0.3.0 differs from the program version 12.1.0.2.0. Instead run the program from /u01/app/oracle/product/11.2.0.3.
[oracle@testdbn01 (qa2tstp1) dbs]$
[oracle@testdbn01 (qa2tstp1) dbs]$ srvctl modify database -d qa2tstp -pwfile '+DATA/QA2TSTP/PASSWORD/pwdqa2tstp.9085.127745674'
[oracle@testdbn01 (qa2tstp1) dbs]$


By using the correct name fixed this error.


Monday, September 25, 2017

ERROR: Unable to get logical block size for spfile '+DATA/dev1tstp/spfiledev1tstp.ora'.

received this error while performing the import on a RAC database.

The issue is that init<SID>.ora file has value of

SPFILE= '+DATA/dev1tstp/spfiledev1tstp.ora'

where as when we look at the ASM and srvctl config database -d dev1tstp

The SPFILE is

 '+DATA/dev1tstp/spfiledev1tst.ora'

change the init<SID>.ora to the value form srvctl config and it fixed the issue.


Monday, September 18, 2017

ORA-19505: failed to identify file "+DATA/dev1tst/spfiledev1tstp.ora"

I am trying to duplicate database for active dataguard but getting the error below:

Starting backup at 12-SEP-17
released channel: ch1
released channel: ch2
released channel: ch3
released channel: ch4
released channel: ch5
released channel: stby1
released channel: stby2
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 09/12/2017 11:30:19
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
RMAN-03009: failure of backup command on ch2 channel at 09/12/2017 11:30:19
ORA-19505: failed to identify file "+DATA/dev1tst/spfiledev1tstp.ora"
ORA-15173: entry 'dev1tst' does not exist in directory '/'

RMAN>


Look at the line in red and blue it holds the answer.

I had a look under $ORACLE_HOME/dbs and tried to find initdev1tstp1.ora file to see the contents and those were +DATA/dev1tst/spfiledev1tstp.ora . When I had a look at the database on primary side its db_unique_name is dev1tstp and that is the directory on ASM as well.

Let me try to explain the message a little bit
ORA-19505: failed to identify file "+DATA/dev1tst/spfiledev1tstp.ora"
ORA-15173: entry 'dev1tst' does not exist in directory '/'

What oracle is trying to inform is that for entry 'dev1tst' there is no such directory under "+DATA", please note that '/' is little misleading here and you could end up looking on file system but actually it was on ASM in my case.

After fixing the init file to the one below, that error disappeared.

[oracle@tser01n01 (dev1tstp1) dbs]$ cat initdev1tstp1.ora
SPFILE='+DATA/dev1tstp/spfiledev1tstp.ora'
[oracle@tser01n01 (dev1tstp1) dbs]$





Friday, August 4, 2017

RMAN-06034: at least 1 channel must be allocated to execute this command

Sometimes we are in hurry and overlook at things or copy paste the scripts and get into all sorts of troubles.

I was trying to speed up the creation of dataguard instance for our huge database and my script was erroring

RMAN-06034: at least 1 channel must be allocated to execute this command

Now what is this I did the things alright but why I am getting the error.

My script was :

RMAN> run {
2> allocate auxiliary channel ch1 type disk;
3> allocate auxiliary channel ch2 type disk;
4> allocate auxiliary channel ch3 type disk;
5> allocate auxiliary channel ch4 type disk;
6> allocate auxiliary channel stby1 type disk;
7> allocate auxiliary channel stby2 type disk;
8> allocate auxiliary channel stby3 type disk;
9> allocate auxiliary channel stby4 type disk;
10> duplicate target database for standby from active database
11> spfile
12> set audit_file_dest='/u01/app/oracle/admin/dev1tstd/adump'
13> set db_unique_name='dev1tstd'
14> set fal_server='dev1tstp1','dev1tstp2'
15> set instance_number='1'
16> NOFILENAMECHECK;
17> }


and error was

Starting backup at 03-AUG-17
released channel: ch1
released channel: ch2
released channel: ch3
released channel: ch4
released channel: stby1
released channel: stby2
released channel: stby3
released channel: stby4
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 08/03/2017 11:41:11
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
RMAN-06034: at least 1 channel must be allocated to execute this command

After having a closer look what I have found the issue. I was only allocating the auxiliary channels and that is what the error was saying as well. So it was fixed by 

run {
allocate channel ch1 type disk;
allocate channel ch2 type disk;
allocate channel ch3 type disk;
allocate channel ch4 type disk;
allocate auxiliary channel stby1 type disk;
allocate auxiliary channel stby2 type disk;
allocate auxiliary channel stby3 type disk;
allocate auxiliary channel stby4 type disk;
duplicate target database for standby from active database
spfile
set audit_file_dest='/u01/app/oracle/admin/dev1tstd/adump'
set db_unique_name='dev1tstd'
set fal_server='dev1tstp1','dev1tstp2'
set instance_number='1'
NOFILENAMECHECK;
}


Allocating more channels definitely helped my creating the dataguard instance much quicker but I still have to test how helpful were those multiple auxiliary channels.


ORA-27191: sbtinfo2 returned error

While creating the standby database from active database with

duplicate target database for standby from active database
spfile
set audit_file_dest='/u01/app/oracle/admin/dev1tstd/adump'
set db_unique_name='dev1tstd'
set fal_server='dev1tstp1','dev1tstp2'
set instance_number='1'
NOFILENAMECHECK;

I was getting the error:

RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 08/03/2017 10:48:17
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
ORA-27191: sbtinfo2 returned error
Additional information: 3475


Please note that I am running the duplicate database from primary database. 

The reason for this issue was simple, I had a wrong configuration setup in RMAN for primary and that was 

CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE';

I had to clear it so that it starts using disk a device 

CONFIGURE DEFAULT DEVICE TYPE clear;

This fixed the issue.

ORA-17628: Oracle error 19505 returned by remote Oracle server

I am trying to rebuild dataguard for a recently refreshed database in our development environment.

While creating the duplicate database from active database using RMAN I am getting the error:

RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 08/03/2017 09:58:18
RMAN-05501: aborting duplication of target database
RMAN-03015: error occurred in stored script Memory Script
RMAN-03009: failure of backup command on ORA_DISK_1 channel at 08/03/2017 09:58:18
ORA-17628: Oracle error 19505 returned by remote Oracle server

As the error says that there are some issues with the remote database and that in my case is the new DR database.

While looking at the alert logs of the DR database what I have found that I was getting the error as below:

NOTE: failed to open SPFILE +DATA/dev1tstd/parameterfile/spfiledev1tstd.ora

Reason was that $ORACLE_HOME/dbs already had an initdev1tstd1.ora file which stores spfile location and that was going to +DATA/dev1tstd/parameterfile/spfiledev1tstd.ora . After the refresh of primary database what I did was rm -rf +DATA/dev1tstd and rm -rf +FRA/dev1tstd which wiped the spfile.

To fix it I just had to rename the initdev1tstd1.ora to initdev1tstd1.ora.old and try the duplicate command again and it fixed the issue.


ORA-00371: not enough shared pool memory, should be atleast 1034490288 bytes

I am trying to setup dataguard for one of our database and while starting the DG instance in nomount mode I am getting an ORA-00371 error

SQL> startup nomount pfile='inittst_aux.ora';
ORA-00371: not enough shared pool memory, should be atleast 1034490288 bytes
SQL>
SQL> !cat inittst_aux.ora
db_name=dev1tstp
SQL> 

This is a bug in Oracle 11.2 and as a workaround what I have done is added a parameter sga_target to ensure that instance has enough memory to start with

[oracle@comp01 (dev1tstd1) dbs]$ cat inittst_aux.ora
db_name=dev1tstp
sga_target=2560M
[oracle@comp01 (dev1tstd1) dbs]$ 

After setting up the parameter the instance started alright.

[oracle@comp01 (dev1tstd1) dbs]$ sqlplus "/ as sysdba"
 set pages 10000 lines 270 long 10000
SQL*Plus: Release 11.2.0.3.0 Production on Thu Aug 3 09:48:14 2017
Copyright (c) 1982, 2011, Oracle.  All rights reserved.
Connected to an idle instance.
SQL> SQL>
SQL>
SQL> startup nomount pfile='inittst_aux.ora';
ORACLE instance started.
Total System Global Area 2672361472 bytes
Fixed Size                  2231352 bytes
Variable Size             654312392 bytes
Database Buffers         1979711488 bytes
Redo Buffers               36106240 bytes
SQL> 


Tuesday, August 1, 2017

How to install xclock on raspberry pi

Sometimes we need x session to raspberry pi and for that we need to have X apps installed on raspberry pi.

I am getting the error that xclock is not installed

pi1@rasp:~/temp$ xclock
-bash: xclock: command not found
pi1@rasp:~/temp$

So I tried installing the xclock and received the message that I should install x11-apps

pi1@rasp:~/temp$ sudo apt-get install xclock
Reading package lists... Done
Building dependency tree      
Reading state information... Done
Package xclock is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
However the following packages replace it:
  x11-apps
E: Package 'xclock' has no installation candidate
pi1@rasp:~/temp$ 


Well that is the solution, so I went on installing x11-aps


pi1@rasp:~/temp$ sudo apt-get install x11-apps
Reading package lists... Done
Building dependency tree    
.
.
.
Processing triggers for libc-bin (2.19-18+deb8u10) ...
Processing triggers for systemd (215-17+deb8u7) ...
pi1@rasp:~/temp$ 


After the installation I just made sure that my DISPLAY variable is set to correct value and xauth is set as well.

pi1@rasp:~/temp$ echo $DISPLAY
localhost:10.0
pi1@rasp:~/temp$
pi1@rasp:~/temp$
pi1@rasp:~/temp$ xauth list | grep -i '10.'
raspberrypi/unix:10  MIT-MAGIC-COOKIE-1  527dd72ade8c37eed6066df0ea8d33bf
pi1@rasp:~/temp$ 


I am also running xming with default settings on client, after running the xclock on terminal I got

pi1@rasp:~/temp$ xclock
Warning: Missing charsets in String to FontSet conversion







Monday, July 31, 2017

kernel: [ 1636.320383] w1_master_driver w1_bus_master1: Family 0 for 00.f80000000000.b6 is not registered.

Spent nearly a day trying to figure out that why my temperature sensor is not working and giving the below message in /var/log/syslog

kernel: [ 1636.320383] w1_master_driver w1_bus_master1: Family 0 for 00.f80000000000.b6 is not registered.

My config.txt settings are as below:

pi@raspberrypi:~ $ grep -i 'dtoverlay' /boot/config.txt
#dtoverlay=lirc-rpi
dtoverlay=w1-gpio
pi@raspberrypi:~ $ 

Nothing special in /etc/module

pi@raspberrypi:~ $ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
snd-bcm2835
#w1-gpio pullup=1
#w1-therm
pi@raspberrypi:~ $ 

but I was still getting the error in the logs.

Now I am getting the link of 28.XXXXXXX but before I was getting 00.XXXXXXXX . So I was not sure that what is happening and what is causing the temp sensor not to work.

Current output is

pi@raspberrypi:/sys/bus/w1/devices $ ls -l
total 0
lrwxrwxrwx 1 root root 0 Jul 31 20:53 28-041600c817ff -> ../../../devices/w1_bus_master1/28-041600c817ff
lrwxrwxrwx 1 root root 0 Jul 31 20:56 w1_bus_master1 -> ../../../devices/w1_bus_master1
pi@raspberrypi:/sys/bus/w1/devices $ 


pi@raspberrypi:~ $ dmesg | grep w1
[    5.663840] w1-gpio onewire@0: gpio pin 4, external pullup pin -1, parasitic power 0
[    5.663887] w1_add_master_device: set_pullup requires write_byte or touch_bit, disabling
pi@raspberrypi:~ $ 


I have spent time reading device tree, checking different gpio pins, updating and upgrading os anything and everything.

In the end I thought of having a closer look then found that the resistor 4.7K Ohm is not connected to one side of the sensor properly, which was causing the abnormal behaviour.

After ensuring the proper connection things have started working okay and now I can see the temp again.

pi@raspberrypi:/sys/bus/w1/devices/28-041600c817ff $ cat w1_slave
9b 00 4b 46 7f ff 0c 10 e1 : crc=e1 YES
9b 00 4b 46 7f ff 0c 10 e1 t=9687
pi@raspberrypi:/sys/bus/w1/devices/28-041600c817ff $ 

Tuesday, July 18, 2017

ORA-03297: file contains used data beyond requested RESIZE value

SQL> alter database tempfile '+DATA/testp/tempfile/temp.276.799247671' resize 5000M;
alter database tempfile '+DATA/testp/tempfile/temp.276.799247671' resize 5000M
*
ERROR at line 1:
ORA-03297: file contains used data beyond requested RESIZE value


SQL>

SQL> col file_name format a70
SQL> select file_name, tablespace_name, status, BYTES/1024/1024 "F Size MB", maxbytes/1024/1024 "Max Size MB", INCREMENT_BY, AUTOEXTENSIBLE from dba_temp_files;

FILE_NAME                                                              TABLESPACE_NAME                STATUS   F Size MB Max Size MB INCREMENT_BY AUT
---------------------------------------------------------------------- ------------------------------ ------- ---------- ----------- ------------ ---
+DATA/testp/tempfile/temp.276.799247671                              TEMP                           ONLINE       11000       11000           80 YES

SQL>

SQL> ALTER TABLESPACE temp SHRINK SPACE KEEP 5000M;

Tablespace altered.

SQL> col file_name format a70
SQL> select file_name, tablespace_name, status, BYTES/1024/1024 "F Size MB", maxbytes/1024/1024 "Max Size MB", INCREMENT_BY, AUTOEXTENSIBLE from dba_temp_files;

FILE_NAME                                                              TABLESPACE_NAME                STATUS   F Size MB Max Size MB INCREMENT_BY AUT
---------------------------------------------------------------------- ------------------------------ ------- ---------- ----------- ------------ ---
+DATA/testp/tempfile/temp.276.799247671                              TEMP                           ONLINE  10999.9922       11000           80 YES

SQL>


SQL> ALTER TABLESPACE temp SHRINK SPACE KEEP 5000M;

Tablespace altered.

SQL> select file_name, tablespace_name, status, BYTES/1024/1024 "F Size MB", maxbytes/1024/1024 "Max Size MB", INCREMENT_BY, AUTOEXTENSIBLE from dba_temp_files;

FILE_NAME                                                              TABLESPACE_NAME                STATUS   F Size MB Max Size MB INCREMENT_BY AUT
---------------------------------------------------------------------- ------------------------------ ------- ---------- ----------- ------------ ---
+DATA/testp/tempfile/temp.276.799247671                              TEMP                           ONLINE  6231.99219       11000           80 YES

SQL>
SQL>
SQL>
SQL> alter database tempfile '+DATA/testp/tempfile/temp.276.799247671' AUTOEXTEND on maxsize 5000M;

Database altered.

SQL> select file_name, tablespace_name, status, BYTES/1024/1024 "F Size MB", maxbytes/1024/1024 "Max Size MB", INCREMENT_BY, AUTOEXTENSIBLE from dba_temp_files;

FILE_NAME                                                              TABLESPACE_NAME                STATUS   F Size MB Max Size MB INCREMENT_BY AUT
---------------------------------------------------------------------- ------------------------------ ------- ---------- ----------- ------------ ---
+DATA/testp/tempfile/temp.276.799247671                              TEMP                           ONLINE  6231.99219  6231.99219           80 YES

SQL>

Monday, July 10, 2017

getting python webpage to be displayed

I am using Server version: Apache/2.4.10 (Raspbian) and Python 2.7.9

the issue I was getting that when trying to open my webpage

http://dhillon.duckdns.org:17581/cgi-bin/webgui.py

It was displaying the python code rather than the webpage.

I had to do below thing to get it fixed.
sudo a2enmod cgi
sudo a2disconf serve-cgi-bin
then modified the conf file under
/etc/apache2/apache2.conf
and included
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin/">
        AddHandler cgi-script .py
        AllowOverride None
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Require all granted
</Directory>
after that restarted the apache and that's it .
sudo service apache2 reload

Make sure that the python files are under /usr/lib/cgi-bin/ and have correct permissions

Done !!!

Wednesday, June 14, 2017

RMAN Reset a parameter

In order to reset the parameter user keyword clear


CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE';


RMAN> CONFIGURE DEFAULT DEVICE TYPE clear;

old RMAN configuration parameters:
CONFIGURE DEFAULT DEVICE TYPE TO 'SBT_TAPE';
RMAN configuration parameters are successfully reset to default value

RMAN> 


New value is 

CONFIGURE DEFAULT DEVICE TYPE TO DISK; # default

RMAN get DB ID

Simple command to get the DB ID or database

RMAN> list db_unique_name of database QA1TST;


List of Databases
DB Key  DB Name  DB ID            Database Role    Db_unique_name
------- ------- ----------------- ---------------  ------------------
8320622 QA1TST   4251438513       PRIMARY          QA1TSTP            
8320622 QA1TST   4251438513       STANDBY          QA1TSTD            

RMAN> 

RMAN-06729: no backup of the SPFILE found to restore

Trying to restore controlfile and getting the error.

RMAN> run {
2> allocate channel c1 type 'sbt_tape' PARMS="SBT_LIBRARY=/opt/simpana/Base/libobk.so,BLKSIZE=262144" TRACE 0;
3> restore spfile to '+DATA/qa1tstp/spfileqa1tstp.ora';
4> }

allocated channel: c1
channel c1: SID=7 device type=SBT_TAPE
channel c1: CommVault Systems for Oracle: Version 10.0.0(BUILD116)

Starting restore at 14-JUN-17

released channel: c1
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of restore command at 06/14/2017 09:57:42
RMAN-06026: some targets not found - aborting restore
RMAN-06729: no backup of the SPFILE found to restore

RMAN>


Fixed it by specifying restore spfile from autoback. It actually went through every day and tried to restore the spfile.

RMAN> run {
2> allocate channel c1 type 'sbt_tape' PARMS="SBT_LIBRARY=/opt/simpana/Base/libobk.so,BLKSIZE=262144" TRACE 0;
3> restore spfile from autobackup;
4> }

allocated channel: c1
channel c1: SID=7 device type=SBT_TAPE
channel c1: CommVault Systems for Oracle: Version 10.0.0(BUILD116)

Starting restore at 14-JUN-17

channel c1: looking for AUTOBACKUP on day: 20170614
channel c1: looking for AUTOBACKUP on day: 20170613
channel c1: looking for AUTOBACKUP on day: 20170612
channel c1: looking for AUTOBACKUP on day: 20170611
channel c1: looking for AUTOBACKUP on day: 20170610
channel c1: looking for AUTOBACKUP on day: 20170609
channel c1: AUTOBACKUP found: c-4251738512-20170609-03
channel c1: restoring spfile from AUTOBACKUP c-4251738512-20170609-03
channel c1: SPFILE restore from AUTOBACKUP complete
Finished restore at 14-JUN-17
released channel: c1

RMAN>

Thursday, May 18, 2017

Standby database notes

To start the recovery process:

SQL> alter database recover managed standby database disconnect from session;
The disconnect from session places the recovery in the background, returning you to the SQL prompt.

To start the managed recover using Real Time Apply, use:
SQL> alter database recover managed standby database using current logfile;


To stop the managed recovery:
SQL> alter database recover managed standby database cancel;



To open the standby database, first stop the managed recovery process using the above command.
Then open the database. Because the control file is a standby control file, the database will open read only.
SQL> alter database recover managed standby database cancel;
SQL> alter database open;
            Or

SQL> alter database open read only;


To close the database and return it to managed recovery simply restart the recovery process.
SQL> alter database recover managed standby database using current logfile;




To startup the standby database simply start the database in a mount mode.  Because the standby uses a standby control file it will automatically mount as a standby database.

SQL> startup mount;
If you start the database using just the startup command the database will automatically open read only. Once you start the managed recovery process, the database will close to a mount state and begin recovery.
With 11g you can configure the database to open read only and continue to recover changes while the database is open.  To do this you must cancel the recovery process, open the standby, and then restart the recovery process.

SQL>  alter database recover managed standby database cancel;
SQL>  alter database open;
SQL>  alter database recover managed standby database disconnect from session;




Flashback Database is a technology introduced in Oracle 10g.  Its function is to move the entire database back in time.  If you flashback the primary database, you must also flashback the standby database.  This is relatively simple as the standby is a copy of the production using the same scns.  If you flashback the primary to an scn, then you must flashback the standby to the same or an earlier scn.  Again, if flashback is active on the primary, then it must also be active on the standby.  This is one of the few changes that does not propagate from the primary to the standby.  This is because turning on flashback is executed in the control file, which does not get passed to the standby.  So it you turn on flashback on the primary, you must also turn it on for the standby.

To flashback the primary database one hour:

SQL> SHUTDOWN IMMEDIATE
SQL> STARTUP MOUNT EXCLUSIVE
SQL> FLASHBACK DATABASE TO TIMESTAMP(SYSDATE-(1/24);
SQL> ALTER DATABASE OPEN RESETLOGS;
To flashback the standby execute the same commands.

SQL> SHUTDOWN IMMEDIATE
SQL> STARTUP MOUNT EXCLUSIVE
SQL> FLASHBACK DATABASE TO TIMESTAMP(SYSDATE-(2/24);
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE;
To turn on flashback use the following commands;

SHUTDOWN IMMEDIATE
STARTUP MOUNT EXCLUSIVE
ALTER DATABASE FLASHBACK ON;
ALTER DATABASE OPEN; ALTER DATABASE RECOVER?





One of the drawbacks of a Managed Standby is that if you actually need to temporarily use the standby in a read write mode, you would have to rebuild the standby because opening the database read write requires an open reset logs, creating a new instantiation. However in 10g and beyond, you can use flashback database to return the standby database to a point in time before you opened it read write.  To perform this action you must first create a restore point before opening the database.  This is the point that we will flash the database back to.  You can also use a timestamp or an scn but a restore point is much easier.  Either of the command below will create a restore point called ?before_open?.

SQL> create restore point before_open;
SQL> create restore point before_open guarantee flashback database;
You cannot create a restore point on the primary database and using it on the standby database.  The restore point must be created on the standby database.



Now you can open the database read write.

SQL> alter database recover managed standby database finish force;
SQL> ALTER DATABASE ACTIVATE STANDBY DATABASE;
SQL> alter database open;
Or

SQL> alter database commit to switchover to primary;
SQL> STARTUP MOUNT FORCE;
SQL> alter database open;
To return the database to a managed standby you will:

1.       Shutdown the open standby database.

2.       Startup mount.

3.       flashback the database to the restore point.

4.       convert the control file back to a standby control file.

5.       startup mount

6.       restart managed recovery.


The actual commands look like this:

SQL> shutdown abort;
SQL> startup mount;
SQL> flashback database to restore point before_open;
SQL> alter database convert to physical standby;
SQL> startup mount force;
SQL> alter database recover managed standby?





Monitoring the Physical Standby



To insure the standby database is ready to perform a switchover or failover you must monitor the recovery progress.  You can get information about the recovery process from a number of sources to include the alert log on both the primary and standby, the dbconsole and v$archived_log.



To insure the managed recover process is running:

Select
   PROCESS,
   SEQUENCE#,
   STATUS
From
   V$MANAGED_STANDBY;


One of the easiest way to verify that the standby is up to date with the primary is to compare the sequence number of the two database.  The script below will get the last sequence number that was applied on the standby and compare it to the max sequence number of the primary.  This script is for Linux but can be adapted for Windows.

#!/bin/ksh
ORACLE_SID=db06
export ORACLE_SID
ORACLE_HOME=`cat /etc/oratab|grep $ORACLE_SID:|cut -f2 -d':'`
export ORACLE_HOME
ORACLE_BASE=/u01/app/oracle
export ORACLE_BASE
$ORACLE_HOME/bin/sqlplus /nolog<<!
connect / as sysdba
set pages 0 heading off feedback off
spool /tmp/standby_applied.lst
select max(sequence#) from v$archived_log where applied = 'YES';
spool off;
connect perfstat/perfstat@DB09.ORACLE.COM
set pages 0 heading off feedback off
spool /tmp/primary_current.lst
select max(sequence#) from v$archived_log;
spool off;
exit
!
applied=`cat /tmp/standby_applied.lst`
current=`cat /tmp/primary_current.lst`
statsvalue=$(($current - $applied))
echo $statsvalue
if [[ $statsvalue -gt 4 ]];
 then
 echo "The Standby Database is falling behind!" > /tmp/stdby_alert.lst
 echo "Standby Applied Sequence: $applied" >> /tmp/stdby_alert.lst
 echo "Primary Current Sequence: $current" >> /tmp/stdby_alert.lst
 echo "Difference:               $statsvalue" >> /tmp/stdby_alert.lst
 echo "" >> /tmp/stdby_alert.lst
 df -h >> /tmp/stdby_alert.lst


 echo "**********************************************************"
 echo "The Standby is NOT Current"
 cat /tmp/stdby_alert.lst |mail -s ?PROD Standby Monitor Alert **"\
     prod_emergency@my_company.net
 echo "**********************************************************"
fi




The script above will provide and email alert if the standby database falls behind the production database by more than four sequence number.  Once scheduled to run hourly in a cron, you will quickly become aware if the standby stops, is not applying redo, or is just falling behind.


Wednesday, May 17, 2017

sysbackup to user to perform the rman backup

The user trying to perform backup should have sysbackup permission under oracle 12.

But we need to setup few things to get it working. We assume that we are going to use username backup_user to create backups. Below are the steps we need to do in order for it to work.

1. Create new password file.
2. Grant sysbackup to backup_user;
3. Make sure rman backup connect has as sysbackup when connecting to target.

Create New Password File:

$ orapwd file=orapwbase sysbackup=y force=y
Enter password for SYS: <<<< Enter OLD / NEW SYS password >>>>
Enter password for SYSBACKUP: <<<< Enter SYSBACKUP password >>>>


SQL> grant sysbackup to backup_user;

$ rman
RMAN> connect target 'backup_user/<password>@base_prod as sysbackup'     <<<<------------    Mandatory
connected to target database: TESTDB (DBID=123456789)
RMAN>


That's it !!!

This way we are only granting the permissions which are required.

RMAN-06820: WARNING: failed to archive current log at primary database

Trying to backup the database but getting an error

RMAN-06820: WARNING: failed to archive current log at primary database

When looked deeper in by looking at the rman logs, it was noticed that I was getting an invalid username and password.

RMAN-06820: WARNING: failed to archive current log at primary database
ORACLE error from target database:
ORA-17629: Cannot connect to the remote database server
ORA-17627: ORA-01017: invalid username/password; logon denied
ORA-17629: Cannot connect to the remote database server


Reason:

This happens because the user trying to perform the backup which is rman in this case does not have sysdba permission

Because of lacking sysdba permission this user does not have entry in password file and can be checked under

select * from v$pwfile_users;


Solution:

It is simple, just grant sysdba to rman and it will fix the issue.

Please note that I know we should grant sysbackup to rman rather than sysdba

It is addressed in another post.

Wednesday, April 5, 2017

ORA-06512: at "SYS.UTL_MAIL", line 654 ORA-06512: at "SYS.UTL_MAIL", line 671

I was getting error while trying to send email from database

SQL> declare
  2  begin
  3  UTL_MAIL.SEND(mime_type => 'text/html; charset=us-ascii',
  4                         sender => 'harvey@example.com',
  5                     recipients => 'harvey@example.com',
  6                        subject => 'Test email Harvey',
  7                        message => 'This is a test email from rac01');
  8  end;
  9  /
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at "SYS.UTL_MAIL", line 654
ORA-06512: at "SYS.UTL_MAIL", line 671
ORA-06512: at line 3


SQL>

What I have found was that the parameter smtp_out_server was not set up

SQL> show parameter  smtp_out_server

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
smtp_out_server                      string
SQL>

By setting the parameter to mail server it worked all good


SQL> alter system set smtp_out_server='mailhost.example.com' scope=both sid='*';

System altered.

SQL> show parameter  smtp_out_server

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
smtp_out_server                      string      mailhost.example.com
SQL> 

SQL> declare
  2  begin
  3  UTL_MAIL.SEND(mime_type => 'text/html; charset=us-ascii',
  4                         sender => 'harvey@example.com',
  5                     recipients => 'harvey@example.com',
  6                        subject => 'Test email Harvey',
  7                        message => 'This is a test email from rac01'); 
  8  end;
  9  /

PL/SQL procedure successfully completed.

SQL> 

Please note that I had to make sure that ACLs are configured right as well

SQL> col acl format a30
SQL> col host format a30
SQL> col principal format a30
SQL> col end_date format a30
SQL> col start_date format a30
SQL> select * from DBA_NETWORK_ACLS;

HOST                           LOWER_PORT UPPER_PORT ACL                            ACLID
------------------------------ ---------- ---------- ------------------------------ --------------------------------
mailhost.csu.edu.au                    25         25 /sys/acls/mailserver_acl.xml   2023FDE3E03E3B45E0511C60040A3C26

SQL> select * from DBA_NETWORK_ACL_PRIVILEGES;

ACL                            ACLID                            PRINCIPAL                      PRIVILE IS_GRANT             INVER START_DATE                     END_DATE
------------------------------ -------------------------------- ------------------------------ ------- -------------------- ----- ------------------------------ ------------------------------
/sys/acls/mailserver_acl.xml   2023FDE3E03E3B45E0511C60040A3C26 HARV                           connect true                 false

SQL> 
SQL> 




Monday, April 3, 2017

Oracle format the output of show parameter command

There are times when you need to show value column with some better formatting. This will not work if you issue

col value for a180

In order to get it working you need to set up value_col_plus_show_param as

SQL> col value_col_plus_show_param for a180
SQL>

Some important show commands:

show parameter
show recyclebin
show sga
show spparameter



SQL> show parameter remote

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
remote_dependencies_mode             string      TIMESTAMP
remote_listener                      string      (ADDRESS_LIST=(ADDRESS=(PROTOC
                                                 OL=TCPS)(HOST=10.2.3.7)(PORT=
                                                 1523))(ADDRESS=(PROTOCOL=TCPS)
                                                 (HOST=10.2.3.8)(PORT=1523))(A
                                                 DDRESS=(PROTOCOL=TCPS)(HOST=10
                                                 .2.3.9)(PORT=1523)))
remote_login_passwordfile            string      EXCLUSIVE
remote_os_authent                    boolean     FALSE
remote_os_roles                      boolean     FALSE
result_cache_remote_expiration       integer     0
SQL>
SQL>
SQL>
SQL> col value_col_plus_show_param for a180
SQL>
SQL> show parameter remote

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
remote_dependencies_mode             string      TIMESTAMP
remote_listener                      string      (ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCPS)(HOST=10.2.3.7)(PORT=1523))(ADDRESS=(PROTOCOL=TCPS)(HOST=10.2.3.8)(PORT=1523))(ADDRESS=(PROTOCOL=TCPS)(HOST=10.2.3.9)(PORT=1523)))
remote_login_passwordfile            string      EXCLUSIVE
remote_os_authent                    boolean     FALSE
remote_os_roles                      boolean     FALSE
result_cache_remote_expiration       integer     0
SQL>
SQL>
SQL>
SQL>
SQL> col value_col_plus_show_param for a80
SQL> show parameter remote

NAME                                 TYPE        VALUE
------------------------------------ ----------- --------------------------------------------------------------------------------
remote_dependencies_mode             string      TIMESTAMP
remote_listener                      string      (ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCPS)(HOST=10.2.3.7)(PORT=1523))(ADDRESS=(PROT
                                                 OCOL=TCPS)(HOST=10.2.3.8)(PORT=1523))(ADDRESS=(PROTOCOL=TCPS)(HOST=10.2.3.9)(P
                                                 ORT=1523)))
remote_login_passwordfile            string      EXCLUSIVE
remote_os_authent                    boolean     FALSE
remote_os_roles                      boolean     FALSE
result_cache_remote_expiration       integer     0
SQL>


In case you want to see all of the current formatting set on sql type column and hit enter it will list all of the column formats.

SQL> column
COLUMN   NAME_COL_PLUS_PDB_RESTRICTED ON
HEADING  'RESTRICTED'
FORMAT   A10

COLUMN   NAME_COL_PLUS_PDB_OPEN_MODE ON
HEADING  'OPEN MODE'
FORMAT   A10

COLUMN   NAME_COL_PLUS_PDB_NAME ON
HEADING  'CON_NAME'
FORMAT   A30
word_wrap

COLUMN   NAME_COL_PLUS_PDB_CON_ID ON
HEADING  'CON_ID'

COLUMN   NAME_COL_PLUS_PDB_CONTAINERID ON
HEADING  'CON_ID'
FORMAT   a30
word_wrap

COLUMN   NAME_COL_PLUS_PDB_CONTAINER ON
HEADING  'CON_NAME'
FORMAT   a30
word_wrap

COLUMN   NAME_COL_PLUS_SHOW_EDITION ON
HEADING  'EDITION'
FORMAT   a30
word_wrap

COLUMN   result_plus_xquery ON
HEADING  'Result Sequence'

COLUMN   other_plus_exp ON
FORMAT   a44

COLUMN   other_tag_plus_exp ON
FORMAT   a29

COLUMN   object_node_plus_exp ON
FORMAT   a8

COLUMN   plan_plus_exp ON
FORMAT   a60

COLUMN   parent_id_plus_exp ON
HEADING  'p'
FORMAT   990

COLUMN   id_plus_exp ON
HEADING  'i'
FORMAT   990

COLUMN   droptime_plus_show_recyc ON
HEADING  'DROP TIME'
FORMAT   a19

COLUMN   objtype_plus_show_recyc ON
HEADING  'OBJECT TYPE'
FORMAT   a12

COLUMN   objectname_plus_show_recyc ON
HEADING  'RECYCLEBIN NAME'
FORMAT   a30

COLUMN   origname_plus_show_recyc ON
HEADING  'ORIGINAL NAME'
FORMAT   a16

COLUMN   SID_COL_PLUS_SHOW_SPPARAM ON
HEADING  'SID'
FORMAT   a8
word_wrap

COLUMN   VALUE_COL_PLUS_SHOW_SPPARAM ON
HEADING  'VALUE'
FORMAT   a28
word_wrap

COLUMN   NAME_COL_PLUS_SHOW_SPPARAM ON
HEADING  'NAME'
FORMAT   a29
word_wrap

COLUMN   value_col_plus_show_param ON
HEADING  'VALUE'
FORMAT   a80

COLUMN   name_col_plus_show_param ON
HEADING  'NAME'
FORMAT   a36

COLUMN   units_col_plus_show_sga ON
FORMAT   a15

COLUMN   name_col_plus_show_sga ON
FORMAT   a24

COLUMN   ERROR ON
FORMAT   A65
word_wrap

COLUMN   LINE/COL ON
FORMAT   A8

COLUMN   ROWLABEL ON
FORMAT   A15
SQL>


Monday, March 6, 2017

Result of my Locus test

Locus of Control Test Results
Internal Locus (84%) Individual believes that their life is defined more by their decisions and internal drive.
External Locus (16%) Individual believes that their life is defined more by genetics, environment, fate, or other external factors.
Take Free Locus of Control Test
personality tests by similarminds.com

Thursday, March 2, 2017

Create a home made temperature sensor using Raspberry pi

Problem we are solving here is.

1. User raspberry pi to get the room temperature
2. Store the temp in a sqlite database.
3. Display the results in a neat graph using a webpage.

Hardware required for this project:
DS18B20 Temperature Sensor
Raspberry Pi
4.7 ohm resistor
Soldering rod
Three wires with female connectors on each end

First of all get your pi ready for the work

-- Install SQLite database

sudo apt-get install sqlite3


-- create a database in the home directory of pi user
sqlite3 mydatabase.db

-- Create table to store the temperature readings
BEGIN;
CREATE TABLE temps (temptime DATETIME, temp NUMERIC);
COMMIT;

-- A tip to get some help
sqlite> .help

-- To exit out of sqlite command prompt
sqlite> .exit


-- create get temp.py to get the temperature
#!/usr/bin/env python

import sqlite3

import os
import time
import glob

# global variables
speriod=(15*60)-1
dbname='/home/pi/tempdb/mydatabase.db'


# store the temperature in the database
def log_temperature(temp):

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    # bavita added , 'localtime' to get the local time from machine else it is showing GMT
    curs.execute("INSERT INTO temps values(datetime('now', 'localtime'), (?))", (temp,))

    # commit the changes
    conn.commit()
    conn.close()


# get temerature
# returns None on error, or the temperature as a float
def get_temp(devicefile):

    try:
        fileobj = open(devicefile,'r')
        lines = fileobj.readlines()
        fileobj.close()
    except:
        return None

    # get the status from the end of line 1 
    status = lines[0][-4:-1]

    # is the status is ok, get the temperature from line 2
    if status=="YES":
        print status
        tempstr= lines[1][-6:-1]
        tempvalue=float(tempstr)/1000
        print tempvalue
        return tempvalue
    else:
        print "There was an error."
        return None

# main function
# This is where the program starts 
def main():

    # enable kernel modules
    os.system('sudo modprobe w1-gpio')
    os.system('sudo modprobe w1-therm')

    # search for a device file that starts with 28
    devicelist = glob.glob('/sys/bus/w1/devices/28*')
    if devicelist=='':
        return None
    else:
        # append /w1slave to the device file
        w1devicefile = devicelist[0] + '/w1_slave'

# while True:

    # get the temperature from the device file
    temperature = get_temp(w1devicefile)
    if temperature != None:
        print "temperature="+str(temperature)
    else:
        # Sometimes reads fail on the first attempt
        # so we need to retry
        temperature = get_temp(w1devicefile)
        print "temperature="+str(temperature)

        # Store the temperature in the database
    log_temperature(temperature)

if __name__=="__main__":
    main()

-- test apache
open the bowser from a machine other than the pi and type the ip address of pi like below and hit enter

http://192.168.0.2/

The page should display something like below:

-- Solder sensor and resistor as described below and then use the wire to connect them to GPIO.

-- The final product should looks something like below:


-- After the setup perform a quick test on sensor and see if it works and that is done as below:

Here temp is 16687 / 1000 = 16.687 C
Please note that if the black sensor is getting too hot then you have soldered it other way around, change the pins and it should be fine.

-- Enable cgi-bin on apache
We will be writing the code in python and in order for that code to display content on webpage we need to change apache config to enable cgi scripts.

Go to /usr/lib/cgi-bin and create sensor.py with chmod 777

Now go to /etc/apache2/sites-available

edit file called default and add below in red color

<Directory "/usr/lib/cgi-bin">
                AddHandler cgi-script .py
                AllowOverride None
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        </Directory>

Note the bit in blue /usr/lib/cgi-bin this is where your web application sensor.py should go and work in browser.


-- code for sensor.py

#!/usr/bin/env python

import sqlite3
import sys
import cgi
import cgitb


# global variables
speriod=(15*60)-1
#dbname='/var/www/mydatabase.db'
dbname='/home/pi/tempdb/mydatabase.db'

# print the HTTP header
def printHTTPheader():
    print "Content-type: text/html\n\n"

# print the HTML head section
# arguments are the page title and the table for the chart
def printHTMLHead(title, table):
    print "<head>"
    print "    <title>"
    print title
    print "    </title>"
    print_graph_script(table)
    print "</head>"

# get data from the database
# if an interval is passed, 
# return a list of records from the database
def get_data(interval):

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    if interval == None:
        curs.execute("SELECT * FROM temps")
    else:
        curs.execute("SELECT * FROM temps WHERE temptime>datetime('now','localtime','-%s hours')" % interval)
#        curs.execute("SELECT * FROM temps WHERE temptime>datetime('2013-09-19 21:30:02','-%s hours') AND temptime<=datetime('2013-09-19 21:31:02')" % interval)

    rows=curs.fetchall()
    conn.close()
    return rows

# convert rows from database into a javascript table
def create_table(rows):
    chart_table=""

    for row in rows[:-1]:
        rowstr="['{0}', {1}],\n".format(str(row[0]),str(row[1]))
        chart_table+=rowstr

    row=rows[-1]
    rowstr="['{0}', {1}]\n".format(str(row[0]),str(row[1]))
    chart_table+=rowstr

    return chart_table


# print the javascript to generate the chart
# pass the table generated from the database info
def print_graph_script(table):

    # google chart snippet
    chart_code="""
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = google.visualization.arrayToDataTable([ ['Time', 'Temperature'],%s ]);
        var options = {
          hAxis: {
          title: 'Time'
        },
        vAxis: {
          title: 'Temperature'
        },
        title: 'Temperature Vs Time',
        backgroundColor: '#f1f8e9'
        };
        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
        chart.draw(data, options);
      }
    </script>"""

    print chart_code % (table)

# print the div that contains the graph
def show_graph():
    print "<h2>Temperature Chart</h2>"
# Bavita commented below to make the graph bigger
#    print '<div id="chart_div" style="width: 900px; height: 500px;"></div>'

    print '<div id="chart_div" style="width: 1300px; height: 700px;"></div>'

# connect to the db and show some stats
# argument option is the number of hours
def show_stats(option):

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    if option is None:
        option = str(24)

    curs.execute("SELECT temptime,max(temp) FROM temps WHERE temptime>datetime('now','localtime','-%s hour') AND temptime<=datetime('now','localtime')" % option)
#    curs.execute("SELECT temptime,max(temp) FROM temps WHERE temptime>datetime('2013-09-19 21:30:02','-%s hour') AND temptime<=datetime('2013-09-19 21:31:02')" % option)
    rowmax=curs.fetchone()
    rowstrmax="{0}&nbsp&nbsp&nbsp{1}C".format(str(rowmax[0]),str(rowmax[1]))

    curs.execute("SELECT temptime,min(temp) FROM temps WHERE temptime>datetime('now','localtime','-%s hour') AND temptime<=datetime('now','localtime')" % option)
#    curs.execute("SELECT temptime,min(temp) FROM temps WHERE temptime>datetime('2013-09-19 21:30:02','-%s hour') AND temptime<=datetime('2013-09-19 21:31:02')" % option)
    rowmin=curs.fetchone()
    rowstrmin="{0}&nbsp&nbsp&nbsp{1}C".format(str(rowmin[0]),str(rowmin[1]))

    curs.execute("SELECT avg(temp) FROM temps WHERE temptime>datetime('now','localtime','-%s hour') AND temptime<=datetime('now','localtime')" % option)
#    curs.execute("SELECT avg(temp) FROM temps WHERE temptime>datetime('2013-09-19 21:30:02','-%s hour') AND temptime<=datetime('2013-09-19 21:31:02')" % option)
    rowavg=curs.fetchone()

    curs.execute("select * from temps where temptime =(select max(temptime)  FROM temps WHERE temptime>datetime('now','localtime','-1 hour') AND temptime<=datetime('now','localtime'))")
    rowcurrent=curs.fetchone()
    rowstrcurrent="{0}&nbsp&nbsp&nbsp{1}C".format(str(rowcurrent[0]),str(rowcurrent[1]))


    print "<hr>"
    print "<h2>Current temperature&nbsp</h2>"
    print rowstrcurrent
    print "<h2>Minumum temperature&nbsp</h2>"
    print rowstrmin
    print "<h2>Maximum temperature</h2>"
    print rowstrmax
    print "<h2>Average temperature</h2>"
    print "%.3f" % rowavg+"C"

    print "<hr>"

    print "<h2>In the last hour:</h2>"
    print "<table>"
    print "<tr><td><strong>Date/Time</strong></td><td><strong>Temperature</strong></td></tr>"

    #rows=curs.execute("SELECT * FROM temps WHERE temptime>datetime('now','-1 hour') AND temptime<=datetime('now')")
    # display the temp in last hour under "In the last hour"
    rows=curs.execute("SELECT * FROM temps WHERE temptime>datetime('now','localtime','-1 hour') AND temptime<=datetime('now','localtime')")
#    rows=curs.execute("SELECT * FROM temps WHERE temptime>datetime('2013-09-19 21:30:02','-1 hour') AND temptime<=datetime('2013-09-19 21:31:02')")
    for row in rows:
        rowstr="<tr><td>{0}&emsp;&emsp;</td><td>{1}C</td></tr>".format(str(row[0]),str(row[1]))
        print rowstr
    print "</table>"

    print "<hr>"

    conn.close()

def print_time_selector(option):

    print """<form action="/cgi-bin/webgui.py" method="POST">
        Show the temperature logs for  
        <select name="timeinterval">"""

    if option is not None:
        if option == "1":
            print "<option value=\"1\" selected=\"selected\">the last 1 hours</option>"
        else:
            print "<option value=\"1\">the last 1 hours</option>"
        if option == "2":
            print "<option value=\"2\" selected=\"selected\">the last 2 hours</option>"
        else:
            print "<option value=\"2\">the last 2 hours</option>"
        if option == "3":
            print "<option value=\"3\" selected=\"selected\">the last 3 hours</option>"
        else:
            print "<option value=\"3\">the last 3 hours</option>"

        if option == "6":
            print "<option value=\"6\" selected=\"selected\">the last 6 hours</option>"
        else:
            print "<option value=\"6\">the last 6 hours</option>"

        if option == "12":
            print "<option value=\"12\" selected=\"selected\">the last 12 hours</option>"
        else:
            print "<option value=\"12\">the last 12 hours</option>"

        if option == "24":
            print "<option value=\"24\" selected=\"selected\">the last 24 hours</option>"
        else:
            print "<option value=\"24\">the last 24 hours</option>"

        if option == "48":
            print "<option value=\"48\" selected=\"selected\">the last 2 Days</option>"
        else:
            print "<option value=\"48\">the last 2 days</option>"

        if option == "168":
            print "<option value=\"168\" selected=\"selected\">the last 7 Days</option>"
        else:
            print "<option value=\"168\">the last 7 days</option>"

        if option == "336":
            print "<option value=\"336\" selected=\"selected\">the last 14 Days</option>"
        else:
            print "<option value=\"336\">the last 14 days</option>"

        if option == "720":
            print "<option value=\"720\" selected=\"selected\">the last 30 Days</option>"
        else:
            print "<option value=\"720\">the last 30 days</option>"

        if option == "4320":
            print "<option value=\"4320\" selected=\"selected\">the last 180 Days</option>"
        else:
            print "<option value=\"4320\">the last 180 days</option>"

    else:
        print """
            <option value="1">the last 1 hours</option>
            <option value="2">the last 2 hours</option>
            <option value="3">the last 3 hours</option>
            <option value="6">the last 6 hours</option>
            <option value="12">the last 12 hours</option>
            <option value="24" selected="selected">the last 24 hours</option>
            <option value="48">the last 2 Day</option>
            <option value="168">the last 7 Day</option>
            <option value="336">the last 14 Day</option>
            <option value="720">the last 30 Day</option>
            <option value="4320">the last 180 Day</option>
            """

    print """        </select>
        <input type="submit" value="Display">
    </form>"""


# check that the option is valid
# and not an SQL injection
def validate_input(option_str):
    # check that the option string represents a number
    if option_str.isalnum():
        # check that the option is within a specific range
        if int(option_str) > 0 and int(option_str) <= 4320:
            return option_str
        else:
            return None
    else: 
        return None

#return the option passed to the script
def get_option():
    form=cgi.FieldStorage()
    if "timeinterval" in form:
        option = form["timeinterval"].value
        return validate_input (option)
    else:
        return None

# main function
# This is where the program starts 
def main():

    cgitb.enable()

    # get options that may have been passed to this script
    option=get_option()

    if option is None:
        option = str(24)

    # get data from the database
    records=get_data(option)

    # print the HTTP header
    printHTTPheader()

    if len(records) != 0:
        # convert the data into a table
        table=create_table(records)
    else:
        print "No data found"
        return

    # start printing the page
    print "<html>"
    # print the head section including the table
    # used by the javascript for the chart
    printHTMLHead("Raspberry Pi Temperature Logger", table)

    # print the page body
    print "<body>"
    print "<h1>Raspberry Pi Temperature Logger</h1>"
    print "<hr>"
    print_time_selector(option)
    show_graph()
    show_stats(option)
    print "</body>"
    print "</html>"

    sys.stdout.flush()

if __name__=="__main__":
    main()


-- Test URL
Now we can test the URL as

http://192.168.0.2/cgi-bin/sensor.py