LNAMP Shell 部署脚本

LNAMP Shell 部署脚本

学习总结:

这个脚本,早期是出于对个人学习Shell的总结而写,应该有些年头了,目前也在一边学马哥视频的基础上陆续完善,10月初才完成LNAMP环境的分离式部署,并减少整个Shell脚本各部分的依赖关系。

我是网络班13期高级班的学员,因个人做了几年Linux运维,所以目前整个高级班的课程,我是跳着看了集群(LVS + Keepalived)、自动化(Cobbler、Ansible)、监控(Zabbix)、分布式存储(Mogilefs和Glusterfs),系统调优以及Openstack的部分。因目前这几个是我现在这个公司短期内要用的,也是目前我主要关心的几个东西。

7月报的班,8月份岗位变动(被人坑了一把,现在兼了linux 运维的工作,应该也是一种机会吧,可以让我直接把学习的东西,直接用在线上环境上)。

8月底到现在,一直都折腾在之前那个人交接的环境上了(环境相当混乱)。因此,一边看视频,一边就直接用在线上环境了。

Ø  部署了Cobbler的内部源(涉及了CentOS、RedHat、Zabbix、Ubuntu);

Ø  研究了ansible + zabbix,参考了 galaxy上的脚本,在线上部署了zabbix环境;

Ø  修改了早期的LNAMP脚本,调整为可分离式部署,并集成了目前公司在用的个人所写的Git SCM运维方面的脚本。

本来也考虑把Git环境也整进去的,但想想还是算了,折腾不起了,把费脑子的事情还是发在学习上去……

转入主题,Shell脚本的入门是很简单的,但要学好是一个挺长时间的事情的,别把Shell想得太简单,基本的运维脚本,只要是一个会点Linux知识,干过这行的,随便都可以写的出来。一个会写代码,有思想的运维是很可怕的,这也是你的一个竞争力的亮点。

    请问以下功能性需求,如果要使用Shell编程,需要界面UI,如何实现?

假设,线上生产环境的一个系统,因早期的开发设计,导致业务层面有很大的BUG问题(比如说,构成数据库唯一键的部分主键冲突),导致整个业务系统,在数据修改、查询操作时,出现数据不一致,冲突性地问题。因此,想用Shell脚本来实现以下功能:

Ø  数据库的修改(需要考虑冲突,界面UI显示,及修正冲突等等)

Ø  数据库的删除

Ø  数据库的备份(提供记录级别、表级别、数据库级别,可以选择性地备份)

Ø  数据库的还原(表级别,没有数据时实现 insert操作,有时实现update操作)

这个需求是我真实碰到的,请问要实现以上功能,需要使用哪些工具?此外,需要保证整个脚本工作地准确性、可靠性及输出的友好性,并且脚本需要尽量避免重复代码。

    这篇Blog 分享出来的脚本,我个人认为还是有很大的参考价值的。因他算是我一直以为对学习Shell脚本的一个很好地总结。脚本中,可能会存在部分代码装B的嫌疑,因有些很简单能实现的代码,我可能会有点稍微复杂点的方式来写,主要是怕自己以后不用会忘了。实际上你懂得~~~

功能:

function.png

代码说明:

代码使用git 方式管理,代码量(15000+ 行),包括了:

Ø  LNAMP环境部署脚本(多套环境,PHP-FPM)

Ø  基础的运维脚本

Ø  Git运维脚本(命令的方式

脚本代码主要涉及的命令工具:

Ø  awk、sed:这两个不用说了,都是编程语言,shell 进阶编程必须的

Ø  dialog:文本模式下,图形化 UI 设计必须的

Ø  tput:也是一个交互性的、控制屏幕输出的工具

脚本演示安装:

    使用了 script 命令录制了,整个脚本的安装过程。

    在代码包的根目录下有个“demo”的脚本(基于scriptreplay),运行“./demo”即可演示相关安装过程。

本脚本还有很多不完善的地方,另外在分离部署中只对基础的 LNAMP 环境进行测试。仅供学习参考。

下载:百度网盘,链接: http://pan.baidu.com/s/1bnt3uLT,密码: c732。

整个程序分为代码包(30M)和源码包(363M)。

解压运行过程(以 root 执行):

    # tar -xvf mallux_nlnamp.tar

    # tar -xvf m6-lnamp.tar

    # cd lnamp/

    # ./pwdHost

使用范围:CentOS 6.x

使用脚本前,本地基于 Cobbler 完成部署的测试 kickstart 文件。

#platform=x86, AMD64, or Intel EM64T

#version=DEVEL

# Firewall configuration
firewall --enabled --ssh

# Install OS instead of upgrade
install

# Use network installation media
url --url=http://192.168.96.98:88/cblr/links/CentOS-6.5-x86_64

# Root password
rootpw --iscrypted $1$GoXayw5C$TlwEAmfGfXIwTeZgseAel0

# System authorization information
auth --useshadow  --passalgo=sha512

# Use text mode install
text

# System keyboard
keyboard us

# System language
lang en_US

# SELinux configuration
selinux --permissive

# Do not configure the X Window System
skipx

# Installation logging level
logging --level=info

# Reboot after installation
reboot

# System timezone
timezone --isUtc Asia/Shanghai

# Network information
# Using "old" style networking config. Make sure all MAC-addresses are in cobbler to use the new-style config
network --bootproto=dhcp --device=eth0 --onboot=on


# System bootloader configuration
bootloader --location=mbr --md5pass='$1$8.oYW1$qsN3sdvzytZE0VT/TUph01'

%include /tmp/partitioning

%pre
if grep -q hda$ /proc/partitions
then
    HD1st=hda
else
    HD1st=sda
fi

echo "bootloader --driveorder=${HD1st}" > /tmp/partitioning

cat >> /tmp/partitioning <<EOF
zerombr yes
part /boot --asprimary --fstype ext4 --ondisk=${HD1st} --size=200
part pv.01 --fstype ext4 --ondisk=${HD1st} --grow --size=1
volgroup mainVG pv.01
logvol swap --vgname=mainVG --fstype swap --size=1024 --name=lv_swap
logvol / --vgname=mainVG --fstype ext4 --size=10000 --name=lv_root
EOF

if [ `grep sd.$ /proc/partitions | wc -l` -gt 1 ]
then
    HD2nd=`grep sd.$ /proc/partitions | grep -v $HD1st | awk 'NR==1 {print $NF}'`

	cat >> /tmp/partitioning <<-EOF
	clearpart --drives=${HD1st},${HD2nd} --all --initlabel
	part pv.02 --fstype ext4 --ondisk=${HD2nd} --grow --size=1
	volgroup 2ndVG pv.02
	logvol /DBHome --vgname=mainVG --fstype ext4 --grow --size=1 --name=lv_DBHome
	logvol /home --vgname=2ndVG --fstype ext4 --grow --size=1 --name=lv_home
	EOF
else
	cat >> /tmp/partitioning <<-EOF
	clearpart --drives=${HD1st} --all --initlabel
	logvol /home --vgname=mainVG --fstype ext4 --grow --size=1 --name=lv_home
	EOF
fi


%packages
@additional-devel
@base
@chinese-support
@development
@hardware-monitoring
@legacy-unix
@network-file-system-client
@network-tools
@performance
@server-platform
@system-admin-tools
@system-management-snmp
cmake
expect
git
ipset
iptraf
iptstate
dstat
lm_sensors
nmap
screen
ftp
telnet
tree
-mysql
-mysql-devel
-mysql-lib


%post
for (( i=0;i<=2;i++ ))
do
    ntpdate -b 218.30.114.84 1>>/root/sync_time.log 2>&1
    [ $? -eq 0 ] && break
    sleep 5
done
hwclock --systohc --utc

Mlux="lnamp_system_v2.0.tar"

wget http://192.168.96.98:88/cblr/links/CentOS-6.5-x86_64/lnamp/$Mlux /root
sed -i '/keepcache/ s;\([[:alpha:]=]*\).*;\11;' /etc/yum.conf
#sed -i '/^#Port 22/aPort 10022' /etc/ssh/sshd_config
#sed -i '/22 / s/22/10022/' /etc/sysconfig/iptables
sed -i "7a alias vi='vim'" /root/.bashrc
sed -i "8a alias ll='ls -al'" /root/.bashrc
sed -i "9a alias grep='grep --color=auto'" /root/.bashrc
sed -i 's;\(^ \{1,\}\)\(\[ "$PS1".*\);\1#\2;' /etc/bashrc
sed -i '/^ \{1,\}#\[ "$PS1" = "\\/a \  [ "$PS1" = "\\\\s-\\\\v\\\\\\$ " ] && PS1="[\\[\\e[0;32;1m\\]\\u\\[\\e[0m\\]@\\[\\e[0;36;1m\\]\\h\\[\\e[0m\\] \\[\\e[0;33;1m\\]\\W\\[\\e[0m\\]]\\\\$ "' /etc/bashrc

grep -q 'md5pass' /boot/grub/grub.conf
if [ $? -gt 0 ]
then
    sed -i '/^timeout/a password --md5 $1$8.oYW1$qsN3sdvzytZE0VT/TUph01' /boot/grub/grub.conf
fi

if [ -f '/usr/bin/git' ]
then
	cat >>/etc/profile.d/git.sh<<-EOF
	# bash completion support for core Git.
	if [ -f /etc/bash_completion.d/git ]
	then
	    source /etc/bash_completion.d/git
	fi
	EOF
fi

[ ! -e "/root/.vimrc" ] && {
	cat >>/root/.vimrc<<-EOF
	set pastetoggle=<F9>
	set nobackup
	set noswapfile
	set hlsearch
	set nonumber
	set cindent
	set autoindent
	set shiftwidth=4
	set tabstop=4
	set expandtab
	set softtabstop=4
	set laststatus=2
	set ruler
	set backspace=indent,eol,start

	syntax on
	EOF
}

NIC="/etc/sysconfig/network-scripts"

if [ -e "$NIC/ifcfg-em1" ]
then
    sed -i "/kernel.*quiet/ s/$/& biosdevname=0/" /boot/grub/grub.conf
    rm -rf /etc/udev/rules.d/70-persistent-net.rules

    for i in `seq 1 7`
    do
        emcfg="ifcfg-em$i"
        ethcfg="ifcfg-eth$[i-1]"

        if [ -e "$NIC/$emcfg" ]
        then
            mv $NIC/$emcfg $NIC/$ethcfg
            sed -i "s/em$i/eth$[i-1]/" $NIC/$ethcfg
        fi
    done
fi

eject


%end

 

目录规划:

[root@localhost lnamp]# tree -L 1

.

├── apex.fw                  ### 目录:存放运维脚本

├── archives                 ### 目录:git 维护脚本

├── bin                      ### 目录:存放dialog、tput 等命令

├── conf                     ### 目录:lnamp 环境部分配置文件

├── etc                      ### 目录:系统环境脚本、配置之类的

├── fonts                    ### 目录:Cacti 所需的字体

├── gitclean                 ### 脚本:git reflog / gc 清理动作

├── gitkeep                  ### 脚本:空目录下创建.gitkeep文件,防止 git 不提交空目录

├── install                  ### 脚本:主脚本

├── iptables.recent          ### 脚本:iptables recent 模块,防火墙策略相关

├── luxgz                    ### 目录:lnamp 源码包

├── luxkey                   ### 目录:ssh 用户 key

├── packages                 ### 目录:lnamp 源码包解压后的路径

├── pwdHost                  ### 脚本:linux、mysql用户密码、网络设置

├── README.md                ### git 说明性文档

├── RPMs                     ### 目录:只放了一个YUM epel6 的 RPM 包

├── scripts                  ### 目录:lnamp 环境安装调用,所有脚本存放位置

├── sql                      ### 数据库目录:cacti监控系统、system web管理后台

├── timeset                  ### 脚本:tput控制屏幕输出

├── tmp                      ### 目录:临时输出文档,工作目录

├── updates                  ### 目录:存放Yum 自定义的本地源压缩档

└── version                  ### 脚本:用来修改脚本的作者、E-Mail等信息

 

14 directories, 8 files

 

脚本执行流程:“pwdHost”->“install”

首次运行时,请运行“pwdHost”脚本,这个脚本会询问你一些相关设置,并在tmp下生成一些临时文件,脚本最后会调用“install”脚本进入部署lnamp环境的主界面

“pwdHost”脚本会做如下设置:

Ø  Linux、MariaDB数据库的root密码

Ø  本地新安装或远程数据库连接设置

Ø  本地网络设置

 

相关界面:

01.jpg

图1     pwdHost UI

当运行过 pwdHost脚本后,后续就可直接执行“./install”调用程序主脚本了。如果需要重新修改设置,可重新运行pwdHost脚本。

   运行“install脚本”时,会显示当前系统的时间(timeset脚本控制)。

如果时间不正确,按下一次“ctrl+c”时,进行设置(如图2)。否则可再次按下“ctrl+c”进入下一个安装环节(如图3)。

02.png

图2     timeset 脚本输出 

03.png

图3     选择应用类型

说明:图3这个界面,是以前残留下来的。因我之前的公司是用PHP开发的,auto4s和dprp5s为两个业务系统的普通账号。这个在后续相关UI的选择操作时,除了会在系统上创建相关用户、拷贝用户密钥(luxkey目录下)以外,还会在部署lnamp环境时,根据这个选择,去修改模板配置文件,生成apache所需的业务系统配置文件。Git 用户同理,但 git 用户会多一个操作,解压“archives”下的 git 运维脚本“repo_sync.tgz”到 git 用户下。

图3选择后,会进入到一个询问你是否要更改“root”用户密码的UI。确认后,正式进入部署lnamp主界面(如图4)。

04.png

图4     lnamp 主程界界面

这个不细说的,看着界面上,按相关键进行UI操作进行了,要提的几点如下:

Lnamp环境部署上,如图5所示,每个源码编译的包,都提供了输出编译参数(Debug),编译后可访问的地址(Available URL)、正式编译安装(Install)等选项。

05.png

图5     nginx 安装界面

    数据库提供了mysql(5.1)和mariadb(5.1和5.5),编译时选择字符校对集时,需要注意一下。一般情况下,选择“utf-8”即可。但是,我们线上的 git环境,申核代码这块用了谷歌的gerrit,测试中发现2.5和2.7版本,gerrit无法在数据库编译时指定“utf-8”下工作,gerrit web UI上会有些操作会报错,即使手工创建修改指定指定字符校对集也不行,不知道怎么回事,所以就引入了默认“latin1”字符校对集的编译一项。

整个 LNAMP 环境部署完毕以后,相关可用的链接地址,就不贴了。相关编译过程中,都有界面可查看。

LNAMP 环境部署,所需的相关脚本(图6),环境都是基于源码编译。

06.png

图6     LNAMP 部署脚本

    文件说明:

        mlux.pwd:密码定义文件,其它一些数据库、普通用户、web 登陆用户密码设置。

        default.pwd:默认linux和mysql的root用户密码,在没有运行“pwdHost”脚本,直接调用“install”脚本时,默认将使用该文件中的root用户默认密码。否则,使用pwdHost脚本设置的密码。

        其它文件,_memu 结尾的为 UI上的界面部分,其它 _ins 等都是程序用到的脚本定义部分。这些脚本,都被根目录下的“install”脚本引用了。

    源码包(如图7),基本全是官方下载的,不过我有强迫怔。在本地解压后,更改属组用户为 root 后,重新打包了,-_##!

blob.png 

图7     源码包

    源码包说明:

        php-5.2.17.tar.gz:集成了 php-fpm 补丁。

    最后,附上文章开始提到的那个数据库需求的脚本,因涉及到数据库,就只贴代码了,仅供参考。脚本中大量应用了 dialog、sed 和 awk部分,应该算是 dialog 学习的一个好脚本。

#!/usr/bin/env bash
### --------------------------------------------------
### Filename:       imgen_update
### Revision:       latest stable
### Author:         Mallux
### E-mail:         gbmagic@aliyun.com
### Blog:           blog.mallux.org
### Description:    Update IMGEN System Product Information
### --------------------------------------------------
### Copyright © 2014-2015 Mallux

#------------
# Exit Operation
#------

trap "__clean_up" EXIT

__clean_up() {
    exec 6>&-
    rm -rf $tmp_dir
}

#------------
# Shell - Global Variable and Function Settings
#------

Author="Mallux"
Email="gbmagic@aliyun.com"

pid=$$
tmp_dir="/tmp/imgen_tools/$pid" ; mkdir -p $tmp_dir ; chmod 1777 ${tmp_dir%/*} &> /dev/null
tmp_fifo="$tmp_dir/__$pid.fifo"
mysql_dump="mysqldump"
mysql_bin="mysql"

### Backup level and Operation Table
backup_level=( db_level table_level record_level )
imgen_table=( imgen_product imgen_ref imgen_tac )
backup_dir="./0-bak"
db_level_dir="$backup_dir/db_level"
table_level_dir="$backup_dir/table_level"

### Call mysql Command Function
__d_mysql_do_bin_cmd() {
    $mysql_bin -h localhost -u$dbuser -p$dbpass "$@"
    [ $? -eq 0 ] && return 0 || return 1
}

### Call mysqldump Command Function
__d_mysql_do_dump_cmd() {
    args_1st=$1 ; shift

    if [ x"$args_1st" == x"db_level" ]
    then
        $mysql_dump --opt --master-data=2 --skip-add-drop-table -u$dbuser -p$dbpass "$@"

    elif [ x"$args_1st" == x"table_level" ]
    then
        $mysql_dump --opt --master-data=2 --skip-add-drop-table -c --skip-extended-insert -u$dbuser -p$dbpass "$@"
    fi

    [ $? -eq 0 ] && return 0 || return 1
}

#------------
# Dialog Unit UI Design
#------

__d_dialog_design_UI() {
    mkfifo $tmp_fifo
    exec 6<>$tmp_fifo

    arch=`arch`

    if [ x"$arch" == x"i686" ]
    then
        dgcmd=`pwd`/bin/dialog32

    elif [ x"$arch" == x"x86_64" ]
    then
        dgcmd=`pwd`/bin/dialog64

    else
        dgcmd=`which dialog`
    fi

    [ ! -f "$dgcmd" -o ! -x "$dgcmd" ] && {
        echo -e "\e[33;1mFatal:\e[0m The \"dialog\" command not found or does not have execute permission.\n"
        exit 1
    }

    title="IMGEN System Product Update       Author: $Author      E-mail: $Email"

    ### Dialog msgbox Unit Function
    __d_dialog_design_msgbox_UI() {
        unset exit_state

        ok_label=$1 ; height=$2 ; width=$3 ; shift 3

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --ok-label " $ok_label " \
                --msgbox "$MSG" $height $width

        return $?
    }

    ### Dialog yesno Unit Function
    __d_dialog_design_yesno_UI() {
        unset exit_state

        yes_label=$1 ; no_label=$2 ; height=$3 ; width=$4 ; shift 4

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --defaultno \
                --yes-label " $yes_label " \
                --no-label " $no_label " \
                --yesno "$MSG" $height $width

        return $?
    }

    ### Dialog password Unit Function
    __d_dialog_design_password_UI() {
        unset exit_state

        ok_label=$1 ; cancel_label=$2 ; height=$3 ; width=$4 ; shift 4

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --ok-label " $ok_label " \
                --cancel-label " $cancel_label " \
                --passwordbox "$MSG" $height $width \
                --output-fd 6

        return $?
    }

    ### Dialog inputbox Unit Function
    __d_dialog_design_inputbox_UI() {
        unset exit_state

        ok_label=$1 ;cancel_label=$2 ; height=$3 ; width=$4 ; shift 4

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --ok-label " $ok_label " \
                --cancel-label " $cancel_label " \
                --inputbox "$MSG" $height $width \
                --output-fd 6

        return $?
    }

    ### Dialog textbox Unit Function
    __d_dialog_design_textbox_UI() {
        unset exit_state

        file=$1
        exit_label=$2 ; height=$3 ; width=$4 ; shift 4

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --exit-label " $exit_label " \
                --textbox $file $height $width

        return $?
    }

    ### Dialog menu Unit Function
    __d_dialog_design_menu_UI() {
        unset exit_state

        ok_label=$1 ; cancel_label=$2 ; extra_label=$3 ; height=$4 ; width=$5 ; list_height=$6 ; shift 6

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --ok-label " $ok_label " \
                --cancel-label " $cancel_label " \
                --extra-button \
                --extra-label " $extra_label " \
                --menu "$MSG" $height $width $list_height \
                ${list_items[*]} \
                --output-fd 6

        return $?
    }

    ### Dialog radiolist Unit Function
    __d_dialog_design_radiolist_UI() {
        unset exit_state

        ok_label=$1 ; cancel_label=$2 ; extra_label=$3 ; height=$4 ; width=$5 ; list_height=$6 ; shift 6

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --visit-items \
                --ok-label " $ok_label " \
                --cancel-label " $cancel_label " \
                --extra-button \
                --extra-label " $extra_label " \
                --radiolist "$MSG" $height $width $list_height \
                ${list_items[*]} \
                --output-fd 6

        return $?
    }

    ### Dialog checklist Unit Function
    __d_dialog_design_checklist_UI() {
        unset exit_state

        select_item=$1
        ok_label=$2 ; cancel_label=$3 ; extra_label=$4 ; height=$5 ; width=$6 ; list_height=$7 ; shift 7

        $dgcmd  --clear \
                --backtitle "$title" \
                --title "| $sub_title |" \
                --colors \
                --separate-output \
                --single-quoted \
                --visit-items \
                --ok-label " $ok_label " \
                --cancel-label " $cancel_label " \
                --extra-button \
                --extra-label " $extra_label " \
                --checklist "$MSG" $height $width $list_height \
                ${list_items[*]} \
                2>$select_item

        return $?
    }

    ### Dialog form Unit Function
    __d_dialog_design_form_UI() {

        ### The EOF section include leading TAB characters ( ctrl+v and Tab ), and end with a Space characters.
        cat > $dload_form <<-EOF
		unset exit_state

		$dgcmd  --clear \ 
		        --backtitle "$title" \ 
		        --title "| $sub_title |" \ 
		        --colors \ 
		        --ok-label " $ok_label " \ 
		        --cancel-label " $cancel_label " \ 
		        --extra-button \ 
		        --extra-label " $extra_label " \ 
		        --form "$MSG" $height $width $form_height \ 
		        ${form_items[*]} \ 
		        2>$field_form
		EOF
    }
}

#------------
# Imgen System Product Database Operation
#------

__o_db_operation_UI() {
    ### Enter Database Name Function
    __o_db_operation_input_dbname_UI() {
        sub_title="Welcome to `whoami`"
        MSG="\nEnter \Z1Database\Zn name for connect ( Default: \Z1IMGEN\Zn )"

        ### Call Dialog inputbox UI
        __d_dialog_design_inputbox_UI Next Quit 10 70

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 dbname ; read -u6 EXIT

        ### Quit Button
        if [ x"$exit_state" == x"1" ]
        then
            exit 1

        ### Next Button
        elif [ x"$exit_state" == x"0" ]
        then
            dbname=${dbname:-IMGEN}
            __o_db_operation_input_dbuser_UI
        fi
    }

    ### Enter Database User Function
    __o_db_operation_input_dbuser_UI() {
        MSG="\nEnter Database \Z1User\Zn for Login ( Default: \Z1root\Zn )"

        ### Call Dialog inputbox UI
        __d_dialog_design_inputbox_UI Next Previous 10 70

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 dbuser ; read -u6 EXIT

        ### Previous Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_connect_UI

        ### Next Button
        elif [ x"$exit_state" == x"0" ]
        then
            dbuser=${dbuser:-root}
            __o_db_operation_input_dbpass_UI
        fi
    }

    ### Enter Database Password Function
    __o_db_operation_input_dbpass_UI() {
        MSG="\nEnter Database \Z1$dbuser\Zn Password ( Default: \Z1NULL\Zn )"

        ### Call Dialog password UI
        __d_dialog_design_password_UI Next Previous 10 70

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 dbpass ; read -u6 EXIT

        ### Previous Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_input_dbuser_UI

        ### Next Button
        elif [ x"$exit_state" == x"0" ]
        then
            dbpass=${dbpass:-sqlpass}
        fi
    }

    ### Test Database Connection Function
    __o_db_operation_connect_UI() {
        __o_db_operation_input_dbname_UI

        ### Call mysql Commamd, test Database connectivity
        __d_mysql_do_bin_cmd $dbname -e quit 2>/dev/null

        [ $? -eq 0 ] && dbcon_state="success" || dbcon_state="failure"

        if [ x"$dbcon_state" == x"success" ]
        then
            __o_db_operation_main_menu_UI
        else
            sub_title="$dbname - Database Connection" ; MSG="\nIncorrect configure, Unable to Connect Database."
            __d_dialog_design_msgbox_UI Return 7 70
            __o_db_operation_connect_UI
        fi
    }

    ### Database Operation Menu Type Function
    __o_db_operation_menu_type_UI() {
        menu_type=$1 ; ok_label=$2 ; cancel_label=$3 ; extra_label=$4 ; shift 4

        case $menu_type in
            menu)
                list_height=${#list_items[*]}

                ### Call Dialog menu UI
                __d_dialog_design_menu_UI $ok_label $cancel_label $extra_label $[list_height+9] 70 $list_height
                ;;
            radiolist)
                list_items=( `echo ${list_items[*]} | xargs -n2 | awk '{print $1,$2,"off"}'` )
                list_height=`echo ${list_items[*]} | xargs -n3 | wc -l`

                ### Call Dialog radiolist UI
                __d_dialog_design_radiolist_UI $ok_label $cancel_label $extra_label $[list_height+9] 70 $list_height
                ;;
        esac

        return $?
    }

    ### Database Operation Main Menu Function
    __o_db_operation_main_menu_UI() {
        unset list_items ; declare -a list_items

        list_items=(    "Backup     Operation"
                        "Up-Del     Operation"
                        "Restore    Operation"      )

        list_height=${#list_items[*]}

        sub_title="$dbname - Database Operation" ; width=70
        MSG="- Select Database (\Z1 $dbname \Zn) Operation -"
        MSG=`echo $MSG | sed ':a s/^.\{1,'"$width"'\}$/ & /;ta'`
        MSG="\n$MSG"

        ### Choice Menu Type, menu or radiolist
        __o_db_operation_menu_type_UI radiolist Select Re-connect Quit

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 operation ; read -u6 EXIT

        ### Re-connect Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_connect_UI

        ### Quit Button
        elif [ x"$exit_state" == x"3" ]
        then
            exit 1

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -z "$operation" ] && __o_db_operation_main_menu_UI

            case "$operation" in
                Backup)
                    __o_db_operation_input_tac_id_UI backup
                    ;;
                Up-Del)
                    __o_db_operation_input_tac_id_UI updel
                    ;;
                Restore)
                    __o_db_operation_restore_UI
                    ;;
            esac
        fi
    }

    ### Enter Product TAC_ID Function
    __o_db_operation_input_tac_id_UI() {
        unset product_id tac_id tac_fac tac_ref ref_id product_id_array tac_ref_array tac_id_array
        unset operation ; operation=$1 ; shift

        if [ x"$operation" == x"backup" ]
        then
            sub_title="$dbname - Backup Operation"
            MSG="\nEnter \Z1TAC_ID(s)\Zn to Backup ( Separate them By \Z1comma\Zn )"

        elif [ x"$operation" == x"updel" ]
        then
            sub_title="$dbname - Retrieve Product"
            MSG="\nEnter \Z1TAC_ID\Zn to Retrieve Product ( Default: \Z1NULL\Zn )"
        fi

        ### Call Dialog inputbox UI
        __d_dialog_design_inputbox_UI Next Main-menu 10 70

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 tac_id ; read -u6 EXIT

        ### Main-menu Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_main_menu_UI

        ### Next Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -z "$tac_id" ] && __o_db_operation_input_tac_id_UI $operation
            #tac_id=${tac_id:-014496}            ### Conflict - PRODUCT_ID && TAC_REF  - 0-sql/imgen_2nd.sql
            #tac_id=${tac_id:-867023}            ### Conflict - PRODUCT_ID             - 0-sql/imgen_1st.sql
            #tac_id=${tac_id:-014450}            ### Conflict - TAC_REF                - 0-sql/imgen_1st.sql
            #tac_id=${tac_id:-868858}            ### Conflict - TAC_REF                - 0-sql/imgen_3rd.sql
            #tac_id=${tac_id:-864330}            ### Normal

            case "$operation" in
                backup)
                    __o_db_operation_backup_UI
                    ;;
                updel)
                    __o_db_operation_query_tac_id_UI
                    ;;
            esac
        fi
    }

    ### Database TAC_ID Product Information Query Function
    __o_db_operation_query_tac_id_info() {
        product_info=$1 ; condition=$2 ; shift 2

        ### Table: imgen_product ( 0 ), imgen_ref ( 1 ) and imgen_tac ( 2 )
        product_query="select t.TAC_ID,t.TAC_FAC,p.*,r.TAC_REF,r.REF_ID,r.REF_CREATION_DATE
                              from ${dbname}.${imgen_table[0]} p left join ${dbname}.${imgen_table[1]} r
                              on p.PRODUCT_ID=r.PRODUCT_ID left join ${dbname}.${imgen_table[2]} t on t.TAC_REF=r.TAC_REF
                       where t.TAC_ID=$tac_id"

        if [ x"$condition" == x"normal" ]
        then
            product_query="${product_query}\G"

        elif [ x"$condition" == x"tac_ref_conflict" ]
        then
            product_query="${product_query} and ${query_condition}\G"
        fi

        ### Call mysql Command, generate TAC_ID product information
        __d_mysql_do_bin_cmd -e "$product_query" > $product_info

        [ -f "$product_info" ] && {
            product_id=`awk '/PRODUCT_ID/ { if ( !a[$2]++ ) print $2 }' $product_info | xargs`
            product_id_array=( `echo $product_id` )
            tac_ref=`awk '/TAC_REF/ {print $2}' $product_info | xargs`
            tac_fac=`awk '/TAC_FAC/ { if ( !a[$2]++ ) print $2 }' $product_info | xargs`
            ref_id=`awk '/REF_ID/ { if ( !a[$2]++ ) print $2 }' $product_info | xargs`

            fline_count=`sed -n '$=' $product_info` ; fline_count=${fline_count:-1}

            block_splict=`echo $tac_ref | xargs -n1 | wc -l`
            block_height=$[$fline_count/$block_splict]

            sed -i '1,'"$block_height"' { /PRODUCT_CODE/ { x;H;d } ; /PRODUCT_REF_COM/ { G } }' $product_info
            sed -i ''"$block_height"',$ { /PRODUCT_CODE/ { x;H;d } ; /PRODUCT_REF_COM/ { G } }' $product_info
            sed -i -e "1 s/.*/TAC_ID: $tac_id\n------/" -e "1 s/^/\n/ ; $ G" $product_info
            sed -i -e "/^\*\{6,\}.*row.*\*\{6,\}$/ s/.*//" $product_info
        }
    }

    ### Database TAC_ID Product Information Conflict Detection Function
    __o_db_operation_query_tac_id_UI() {
        product_info="$tmp_dir/__p_${tac_id}.product_info"

        sub_title="$dbname Database - Query Result"

        ### Retrieve TAC_ID product informatin
        __o_db_operation_query_tac_id_info $product_info normal

        if [ -z "$tac_id" -o -z "$product_id" -o -z "$ref_id" ]
        then
            MSG="\nUnable to retrieve TAC_ID (\Z1 $tac_id \Zn) Product information."
            __d_dialog_design_msgbox_UI Re-retrieve 7 70
            __o_db_operation_input_tac_id_UI updel
        fi

        for key in ${!product_id_array[*]}
        do
            if [ x"$key" == x"0" ]
            then
                query_condition="PRODUCT_ID=${product_id_array[$key]}"
            else
                query_condition="$query_condition or PRODUCT_ID=${product_id_array[$key]}"
            fi
        done

        ### Call mysql Command, retrieve PRODUCT_ID related TAC_REF. Table: imgen_ref ( 1 )
        tac_ref_array=( `__d_mysql_do_bin_cmd $dbname -e "select * from ${imgen_table[1]} where ${query_condition}\G" | awk '/TAC_REF: / {print $2}'` )

        ### TAC_REF Conflict Detection ( imgen_tac major key ), Duplicate TAC_REF
        ### Retrieve Process: TAC_ID -> TAC_REF -> PRODUCT_ID
        if [ ${#product_id_array[*]} -gt 1 -a ${#tac_ref_array[*]} -gt 1 ]
        then
            ### Duplicate TAC_REF Number. Don't use tac_ref_array to filter.
            ### Because, the system may detect PRODUCT_ID and TAC_REF conflict at the same time.
            ### The tac_ref value is used to query TAC_REF duplicate items.
            unique_tac_ref=( `echo $tac_ref | xargs -n1 | awk '!a[$0]++ { print $0 }'` )

            MSG="\nDuplicate TAC_REF ( TAC_ID:\Z1 $tac_id \Zn, TAC_FAC:\Z1 $tac_fac \Zn)"
            MSG="$MSG\n\nQuery TAC_ID: \Z1$tac_id\Zn | Conflict TAC_REF:\Z1 ${unique_tac_ref[*]} \Zn"
            MSG="$MSG\n\nConflict Major Key Information:\n------"
            MSG="$MSG\nPRODUCT_ID: \Z1 $(echo $product_id | xargs -n1 | awk '{ print NR" | "$0,"\\ " }' | xargs) \Zn"
            MSG="$MSG\n   TAC_REF: \Z1 $(echo $tac_ref | xargs -n1 | awk '{ print NR" | "$0,"\\ " }' | xargs) \Zn"
            MSG="$MSG\n    REF_ID: \Z1 $(echo $ref_id | xargs -n1 | awk '{ print NR" | "$0,"\\ " }' | xargs) \Zn"
            MSG="$MSG\n\nYou need to do the following Operation.\n------"
            MSG="$MSG\nStep 1. \Z1insert\Zn new tac informatin into imgen_tac table,"
            MSG="$MSG\n        and \Z1generate New\Zn TAC_REF automatically.\n"
            MSG="$MSG\nStep 2. \Z1update\Zn duplicate TAC_REF items on imgent_ref table,"
            MSG="$MSG\n        and \Z1relate New\Zn TAC_REF automatically."

            ### Step 1.1 - Retrieve related TAC_REF, Table: imgen_ref ( 1 )
            tac_ref="${unique_tac_ref[0]}"

            __d_dialog_design_msgbox_UI Next 23 80
            __o_db_operation_conflict_tac_ref_query_UI

        ### PRODUCT_ID Conflict Detection ( imgen_product major key ), Duplicate PRODUCT_ID
        ### Retrieve Process: PRODUCT_ID -> TAC_REF -> TAC_ID
        elif [ ${#product_id_array[*]} -eq 1 -a ${#tac_ref_array[*]} -gt 1 ]
        then
            for key in ${!tac_ref_array[*]}
            do
                if [ x"$key" == x"0" ]
                then
                    query_condition="TAC_REF=${tac_ref_array[$key]}"
                else
                    query_condition="$query_condition or TAC_REF=${tac_ref_array[$key]}"
                fi
            done

            ### Step 2.1 - Call mysql Command, Retrieve related TAC_ID. Table: imgen_tac ( 2 )
            tac_id_array=( `__d_mysql_do_bin_cmd $dbname -sN -e "select TAC_ID from ${imgen_table[2]} where $query_condition;"` )

            conflict_tac_id_file="$tmp_dir/__p_${tac_id}.conflict.tac_id"
            echo ${tac_id_array[*]} | xargs -n1 > $conflict_tac_id_file

            MSG="\nDuplicate PRODUCT_ID (\Z1 $product_id \Zn, TAC_ID:\Z1 ${tac_id_array[*]} \Zn)"
            MSG="$MSG\n\nQuery TAC_ID: \Z1$tac_id\Zn | Conflict TAC_ID: \Z1$(echo ${tac_id_array[*]} | xargs -n1 | grep -v $tac_id)\Zn"
            MSG="$MSG\n\nYou need to do the following Operation.\n------"
            MSG="$MSG\nStep 1. \Z1insert\Zn new product informatin into imgen_product table,"
            MSG="$MSG\n        and \Z1generate New\Zn PRODUCT_ID automatically.\n"
            MSG="$MSG\nStep 2. \Z1update\Zn duplicate PRODUCT_ID items on imgent_ref table,"
            MSG="$MSG\n        and \Z1relate New\Zn PRODUCT_ID automatically."

            __d_dialog_design_msgbox_UI Next 17 80
            __o_db_operation_conflict_product_id_update_UI

        ### No conflict
        else
            ### Call Dialog textbox UI
            __d_dialog_design_textbox_UI $product_info Next $[fline_count+9] 90

            exit_state=$? ; #echo $exit_state ; exit

            ### Next Button
            if [ x"$exit_state" == x"0" ]
            then
                __o_db_operation_menu_updel_UI
            fi
        fi
    }

    ### Database TAC_REF Conflict Operation - Query Filter
    __o_db_operation_conflict_tac_ref_query_UI() {
        field_info="$tmp_dir/__p_${tac_id}.conflict.query_field"
        field_form="$tmp_dir/__p_${tac_id}.conflict.query_form"
        field_data="$tmp_dir/__p_${tac_id}.conflict.query_data"
        dload_form="$tmp_dir/__d_${tac_id}.dialog_design_form_UI"

        sub_title="$dbname - Query Filter"
        MSG="\nQuery TAC_ID: \Z1$tac_id\Zn | Conflict TAC_REF:\Z1 ${unique_tac_ref[*]} \Zn"

        unset list_items ; declare -a list_items
        list_items=( `cat $product_info | awk -F':' '/PRODUCT_/ { if ( !a[$1]++ ) print $1,"field","off" }' | sed '/PRODUCT_CREATION_DATE/d ; s/^ \{1,\}//'` )
        list_height=`echo ${list_items[*]} | xargs -n3 | wc -l`

        ### Call Dialog checklist UI
        __d_dialog_design_checklist_UI $field_info Select Re-Select Main-menu $[list_height+9] 70 $list_height

        exit_state=$? ; #echo $exit_state ; exit

        query_field=( `cat $field_info` ) ; field_height=${#query_field[*]}
        field_max_width=`echo ${query_field[*]} | xargs -n1 | awk '{ if ( w < length($1) ) w = length($1) } END { print w }'`

        ### Re-Select Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_conflict_tac_ref_query_UI

        ### Main-menu Button
        elif [ x"$exit_state" == x"3" ]
        then
            __o_db_operation_main_menu_UI

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ x"$field_height" == x"0" ] && {
                MSG="\n Please select at least \Z1One\Zn field to Query."
                __d_dialog_design_msgbox_UI Return 7 70
                __o_db_operation_conflict_tac_ref_query_UI
            }

            __o_db_operation_conflict_tac_ref_retrieve_UI
        fi
    }

    ### Database TAC_REF Conflict Operation - Retrieve Product
    __o_db_operation_conflict_tac_ref_retrieve_UI() {
        ### The original product information file ( __p_${tac_id}.product_info )
        __product_info="$product_info"

        unset form_items ; declare -a form_items

        ### Append field form, the form look like this: [ label y x item y x flen ilen ] ...
        for key in ${!query_field[*]}
        do
            case "${query_field[$key]}" in
                PRODUCT_NAME|PRODUCT_CODE)
                    form_value="5054W"
                    ;;
                PRODUCT_DESIGNATION|PRODUCT_REF_COM)
                    form_value="Pixi3-5.5 4G"
                    ;;
                PRODUCT_CREATION_DATE)
                    form_value=`date '+%Y-%m-%d %T'`
                    ;;
                PRODUCT_ID)
                    form_value="1698"
                    ;;
            esac

            form_items[$key]="${query_field[$key]}: $[key+1] 1 '$form_value' $[key+1] $[field_max_width+3] 70 200"
        done

        MSG="\nEnter the following information:"

        set -- Retrieve Re-input Return $[field_height+9] 70 $field_height
        ok_label=$1 ; cancel_label=$2 ; extra_label=$3 ; height=$4 ; width=$5 ; form_height=$6 ; shift 6

        ### Call Dialog form UI - load form data
        __d_dialog_design_form_UI ; sed -i '1,$ s/ $//' $dload_form ; source $dload_form

        exit_state=$? ; #echo $exit_state ; exit

        ### Re-input Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_conflict_tac_ref_retrieve_UI

        ### Return Button
        elif [ x"$exit_state" == x"3" ]
        then
            rm -rf $field_info $field_form $dload_form
            __o_db_operation_conflict_tac_ref_query_UI

        ### Retrieve Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -f "$field_info" ] && extra_comma_row=$[$(sed -n '$=' $field_info)+1]

            [ -f "$field_form" ] && {
                sed -i 's/ \{1,\}$//' $field_form
                awk 'ARGIND==1 { a[FNR]=$0 ; next } ARGIND==2 { print a[FNR]"=""'\''"$0"'\''" }' $field_info $field_form > $field_data

                unset query_condition

                while read condition
                do
                    if [ -z "$query_condition" ]
                    then
                        query_condition="p.$condition"
                    else
                        query_condition="$query_condition and p.$condition"
                    fi
                done < $field_data
            }

            query_msg="$tmp_dir/__p_${tac_id}.conflict.query_msg"

            ### Step 1.2 - Retrieve TAC_ID product informatin and related PRODUCT_ID ( imgen_ref )
            __o_db_operation_query_tac_id_info $query_msg tac_ref_conflict

            if [ -z "$tac_id" -o -z "$product_id" -o -z "$ref_id" ]
            then
                MSG="\nUnable to retrieve TAC_ID (\Z1 $tac_id \Zn) Product information."

                ### Reuse the original product information file ( __p_${tac_id}.product_info )
                ### Because there is no query data will cause a script error.
                product_info="$__product_info"

                __d_dialog_design_msgbox_UI Re-retrieve 7 70
                __o_db_operation_conflict_tac_ref_retrieve_UI
            fi

            ### Call Dialog textbox UI
            __d_dialog_design_textbox_UI $query_msg Next $[fline_count+9] 90

            exit_state=$? ; #echo $exit_state ; exit

            ### Next Button
            if [ x"$exit_state" == x"0" ]
            then
                __o_db_operation_conflict_tac_ref_update_UI
            fi
        fi
    }

    ### Database TAC_REF Conflict Operation - Update Product
    __o_db_operation_conflict_tac_ref_update_UI() {
        tacid_info="$tmp_dir/__p_${tac_id}.tacid_info"
        field_info="$tmp_dir/__p_${tac_id}.conflict.insert_field"
        field_form="$tmp_dir/__p_${tac_id}.conflict.insert_form"
        field_data="$tmp_dir/__p_${tac_id}.conflict.insert_data"

        ### Call mysql Command, generate TAC_ID tac Field information. Table: imgen_tac ( 2 )
        __d_mysql_do_bin_cmd $dbname -e "select * from ${imgen_table[2]} where TAC_REF=$tac_ref\G" | sed '1d' > $tacid_info

        awk -F': ' '{print $1}' $tacid_info > $field_info

        unset form_items ; declare -a form_items

        insert_field=( `cat $tacid_info | awk -F':' '{print $1}'` ) ; field_height=${#insert_field[*]}
        field_max_width=`echo ${insert_field[*]} | xargs -n1 | awk '{ if ( w < length($1) ) w = length($1) } END { print w }'`

        ### Append field form, the form look like this: [ label y x item y x flen ilen ] ...
        for key in ${!insert_field[*]}
        do
            case "${insert_field[$key]}" in
                TAC_CREATION_DATE)
                    form_value=`date '+%Y-%m-%d %T'`
                    ;;
                TAC_REF)
                    ### Call mysql Command, Retrieve New TAC_REF. Table: imgen_tac ( 2 )
                    form_value=`__d_mysql_do_bin_cmd $dbname -sN -e "show create table ${imgen_table[2]}\G" | \
                                sed -n '/.*ENGINE=.*AUTO_INCREMENT=\([[:digit:]]*\) .*/ s//\1/p'`
                    new_tac_ref="$form_value"
                    ;;
                *)
                    form_value=`awk -F': ' '/'"${insert_field[$key]}"'/ {print $2}' $tacid_info`
                    ;;
            esac

            form_items[$key]="${insert_field[$key]}: $[key+1] 1 '$form_value' $[key+1] $[field_max_width+3] 70 200"
        done

        sub_title="$dbname - TAC_REF Conflict" ; width=70
        MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
        MSG=`echo $MSG | sed ':a s/^.\{1,'"$[width-1]"'\}$/ & /;ta'`
        MSG="\n$MSG\n\n\Z1Step 1.\Zn insert \Z1New\Zn tac informatin into imgen_tac table."
        MSG="$MSG\n        The \Z1TAC_REF\Zn ( major Key ) is automatically generated."

        set -- Insert Re-input Main-menu $[field_height+12] $width $field_height
        ok_label=$1 ; cancel_label=$2 ; extra_label=$3 ; height=$4 ; width=$5 ; form_height=$6 ; shift 6

        ### Call Dialog form UI
        __d_dialog_design_form_UI ; sed -i '1,$ s/ $//' $dload_form ; source $dload_form

        exit_state=$? ; #echo $exit_state ; exit

        ### Re-input Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_conflict_tac_ref_update_UI

        ### Main-menu Button
        elif [ x"$exit_state" == x"3" ]
        then
            rm -rf $tacid_info $field_info $field_form $dload_form
            __o_db_operation_main_menu_UI

        ### Insert Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -f "$field_info" ] && extra_comma_row=$[$(sed -n '$=' $field_info)+1]

            [ -f "$field_form" ] && {
                sed -i 's/ \{1,\}$//' $field_form
                awk 'ARGIND==1 { a[FNR]=$0 ; next } ARGIND==2 { print a[FNR]":",$0 }' $field_info $field_form > $field_data
                new_tac_id=`awk '/TAC_ID/ {print $2}' $field_data`
                new_tac_fac=`awk '/TAC_FAC/ {print $2}' $field_data`
                new_tac_ref=`awk '/TAC_REF/ {print $2}' $field_data`
            }

            insert_sql="$tmp_dir/__p_${tac_id}.conflict.insert_sql"
            insert_msg="$tmp_dir/__p_${tac_id}.conflict.insert_msg"

            ### Related Process: Generate ( SQL ) -> Insert ( imgen_tac )-> Relate ( imgen_ref )
            ### Step 1.3 - Generate insert DML statement, insert tac informatin into imgen_tac ( 2 ).
            awk -F': ' 'BEGIN { print "insert into '${imgen_table[2]}' (" } \
                        ARGIND==1 { print $0"," ; next } \
                        ARGIND==2 { a[FNR]=$0 ; b[j++]=FNR } \
                        END { print ") values (" ; for (i=0;i<j;i++) print "'\''"a[b[i]]"'\''," }' $field_info $field_form | \
                        sed -e '1,$ s/^ \{1,\}// ; $s/,/ );/' -e ''${extra_comma_row}' s/,$//' > $insert_sql

            [ -f "$insert_sql" ] && sed -e '1,$ s/^/| /' -e '1,$ s/$/\\n/' $insert_sql > $insert_msg

            msg_height=`sed -n '$=' $insert_msg`

            MSG="\nTAC_ID: \Z1$tac_id $tac_fac\Zn -> \Z1$new_tac_id $new_tac_fac\Zn | TAC_REF: \Z1$tac_ref\Zn -> \Z1$new_tac_ref\Zn"
            MSG="$MSG\n\n------"
            MSG="$MSG\n\Z1WARNING:\Zn Are you sure want to perform \Z1Insert\Zn Operation? ( Default: \Z1No\Zn )"
            MSG="$MSG\n\n+------"
            MSG="$MSG\n$(cat $insert_msg)+------"

            ### Call Dialog yesno UI
            __d_dialog_design_yesno_UI Continue Cancel $[msg_height+13] 92

            exit_state=$? ; #echo $exit_state ; exit

            ### Cancel Button
            if [ x"$exit_state" == x"1" ]
            then
                __o_db_operation_input_tac_id_UI updel

            ### Continue Button
            elif [ x"$exit_state" == x"0" ]
            then
                MSG="\nTAC_ID: \Z1$tac_id\Zn -> \Z1$new_tac_id\Zn | TAC_REF: \Z1$tac_ref\Zn -> \Z1$new_tac_ref\Zn"
                MSG="$MSG\n\n\Z1Step 2 :\Zn\n------"

                ### Step 1.4 - Call mysql Command, Insert New tac informain ( Record - Row level, insert into imgen_tac Table )
                __d_mysql_do_bin_cmd $dbname < $insert_sql

                [ $? -eq 0 ] && insert_state="Success" || insert_state="Failure"
                MSG="$MSG\n       imgen_tac \Z1Insert\Zn   -   [\Z1 $insert_state \Zn]"

                ### step 1.5 - Call mysql Command, Update old TAC_REF to New TAC_REF ( Record - Row level, update imgen_ref Table )
                update_sql="update ${imgen_table[1]} set TAC_REF=$new_tac_ref where PRODUCT_ID=$product_id and TAC_REF=$tac_ref and REF_ID=$ref_id;"
                [ x"$insert_state" == x"Success" ] && __d_mysql_do_bin_cmd $dbname -e "$update_sql"

                [ $? -eq 0 ] && update_state="Success" || update_state="Failure"
                MSG="$MSG\n       imgen_ref \Z1Update\Zn   -   [\Z1 $update_state \Zn]"

                MSG="$MSG\n\nIf it was successful, please press the \Z1Re-retrieve\Zn button to continue,"
                MSG="$MSG\nand \Z1Update\Zn product information if necessarily.\n"

                ### Call Dialog msgbox UI
                __d_dialog_design_msgbox_UI Re-retrieve 15 80

                exit_state=$? ; #echo $exit_state ; exit

                ### Re-retrieve Button
                if [ x"$exit_state" == x"0" ]
                then
                    __o_db_operation_input_tac_id_UI updel
                fi
            fi
        fi
    }

    ### Database PRODUCT_ID Conflict Operation - Retrieve and Update
    __o_db_operation_conflict_product_id_update_UI() {
        field_info="$tmp_dir/__p_${tac_id}.conflict.insert_field"
        field_form="$tmp_dir/__p_${tac_id}.conflict.insert_form"
        field_data="$tmp_dir/__p_${tac_id}.conflict.insert_data"
        dload_form="$tmp_dir/__d_${tac_id}.dialog_design_form_UI"

        ### Call mysql Command, generate TAC_ID product Field information. Table: imgen_table ( 0 )
        __d_mysql_do_bin_cmd $dbname -e "select * from ${imgen_table[0]} where PRODUCT_ID=$product_id\G" | sed '1d' | awk -F':' '{print $1}' > $field_info

        sed -i -e '/PRODUCT_CODE/ { h;d } ; /PRODUCT_REF_COM/ { G }' $field_info

        insert_field=( `cat $field_info` ) ; field_height=${#insert_field[*]}
        field_max_width=`echo ${insert_field[*]} | xargs -n1 | awk '{ if ( w < length($1) ) w = length($1) } END { print w }'`

        unset form_items ; declare -a form_items

        ### Append field form, the form look like this: [ label y x item y x flen ilen ] ...
        for key in ${!insert_field[*]}
        do
            case "${insert_field[$key]}" in
                PRODUCT_NAME|PRODUCT_CODE)
                    form_value="7048X"
                    ;;
                PRODUCT_DESIGNATION|PRODUCT_REF_COM)
                    form_value="Go play"
                    ;;
                PRODUCT_CREATION_DATE)
                    form_value=`date '+%Y-%m-%d %T'`
                    ;;
                PRODUCT_ID)
                    ### Call mysql Command, Retrieve New PRODUCT_ID. Table: imgen_product ( 0 )
                    form_value=`__d_mysql_do_bin_cmd $dbname -sN -e "show create table ${imgen_table[0]}\G" | \
                                sed -n '/.*ENGINE=.*AUTO_INCREMENT=\([[:digit:]]*\) .*/ s//\1/p'`
                    new_product_id="$form_value"
            esac

            form_items[$key]="${insert_field[$key]}: $[key+1] 1 '$form_value' $[key+1] $[field_max_width+3] 70 200"
        done

        sub_title="$dbname - PRODUCT_ID Conflict" ; width=70
        MSG="- TAC_ID: \Z1$tac_id\Zn -"
        MSG=`echo $MSG | sed ':a s/^.\{1,'"$[width-1]"'\}$/ & /;ta'`
        MSG="\n$MSG\n\n\Z1Step 1.\Zn insert \Z1New\Zn product informatin into imgen_product table."
        MSG="$MSG\n        The \Z1PRODUCT_ID\Zn ( major Key ) is automatically generated."

        set -- Insert Re-input Main-menu $[field_height+12] $width $field_height
        ok_label=$1 ; cancel_label=$2 ; extra_label=$3 ; height=$4 ; width=$5 ; form_height=$6 ; shift 6

        ### Call Dialog form UI
        __d_dialog_design_form_UI ; sed -i '1,$ s/ $//' $dload_form ; source $dload_form

        exit_state=$? ; #echo $exit_state ; exit

        ### Re-input Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_conflict_product_id_update_UI

        ### Main-menu Button
        elif [ x"$exit_state" == x"3" ]
        then
            rm -rf $field_info $field_form $dload_form
            __o_db_operation_main_menu_UI

        ### Insert Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -f "$field_info" ] && extra_comma_row=$[$(sed -n '$=' $field_info)+1]

            [ -f "$field_form" ] && {
                sed -i 's/ \{1,\}$//' $field_form
                awk 'ARGIND==1 { a[FNR]=$0 ; next } ARGIND==2 { print a[FNR]":",$0 }' $field_info $field_form > $field_data
                new_product_id=`awk '/PRODUCT_ID/ {print $2}' $field_data`
            }

            insert_sql="$tmp_dir/__p_${tac_id}.conflict.insert_sql"
            insert_msg="$tmp_dir/__p_${tac_id}.conflict.insert_msg"

            ### Related Process: Generate ( SQL ) -> Insert ( imgen_product )-> Relate ( imgen_ref )
            ### Step 2.2 - Generate insert DML statement, insert product informatin into imgen_product ( 0 ).
            awk -F': ' 'BEGIN { print "insert into '${imgen_table[0]}' (" } \
                        ARGIND==1 { print $0"," ; next } \
                        ARGIND==2 { a[FNR]=$0 ; b[j++]=FNR } \
                        END { print ") values (" ; for (i=0;i<j;i++) print "'\''"a[b[i]]"'\''," }' $field_info $field_form | \
                        sed -e '1,$ s/^ \{1,\}// ; $s/,/ );/' -e ''${extra_comma_row}' s/,$//' > $insert_sql

            [ -f "$insert_sql" ] && sed -e '1,$ s/^/| /' -e '1,$ s/$/\\n/' $insert_sql > $insert_msg

            msg_height=`sed -n '$=' $insert_msg`

            MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn -> New PRODUCT_ID: \Z1$new_product_id\Zn"
            MSG="$MSG\n\n------"
            MSG="$MSG\n\Z1WARNING:\Zn Are you sure want to perform \Z1Insert\Zn Operation? ( Default: \Z1No\Zn )"
            MSG="$MSG\n\n+------"
            MSG="$MSG\n$(cat $insert_msg)+------"

            ### Call Dialog yesno UI
            __d_dialog_design_yesno_UI Continue Cancel $[msg_height+13] 92

            exit_state=$? ; #echo $exit_state ; exit

            ### Cancel Button
            if [ x"$exit_state" == x"1" ]
            then
                __o_db_operation_input_tac_id_UI updel

            ### Continue Button
            elif [ x"$exit_state" == x"0" ]
            then
                MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn -> New PRODUCT_ID: \Z1$new_product_id\Zn"
                MSG="$MSG\n\n\Z1Step 2 :\Zn\n------"

                ### Step 2.3 - Call mysql Command, Insert New product informain ( Record - Row level, insert into imgen_product Table )
                __d_mysql_do_bin_cmd $dbname < $insert_sql

                [ $? -eq 0 ] && insert_state="Success" || insert_state="Failure"
                MSG="$MSG\n       imgen_product \Z1Insert\Zn   -   [\Z1 $insert_state \Zn]"

                ### step 2.4 - Call mysql Command, Update old PRODUCT_ID to New PRODUCT_ID ( Record - Row level, update imgen_ref Table )
                update_sql="update ${imgen_table[1]} set PRODUCT_ID=$new_product_id where PRODUCT_ID=$product_id and TAC_REF=$tac_ref and REF_ID=$ref_id;"
                [ x"$insert_state" == x"Success" ] && __d_mysql_do_bin_cmd $dbname -e "$update_sql"

                [ $? -eq 0 ] && update_state="Success" || update_state="Failure"
                MSG="$MSG\n       imgen_ref     \Z1Update\Zn   -   [\Z1 $update_state \Zn]"

                MSG="$MSG\n\nIf it was successful, please press the \Z1Re-retrieve\Zn button to Re-retrieve.\n"

                ### Call Dialog msgbox UI
                __d_dialog_design_msgbox_UI Re-retrieve 14 80

                exit_state=$? ; #echo $exit_state ; exit

                ### Re-retrieve Button
                if [ x"$exit_state" == x"0" ]
                then
                    __o_db_operation_input_tac_id_UI updel
                fi
            fi
        fi
    }

    ### Database Operation Up-Del ( include Update and Delete ) Menu Function
    __o_db_operation_menu_updel_UI() {
        unset list_items ; declare -a list_items

        list_items=(    "Update     Operation"
                        "Delete     Operation"      )

        sub_title="$dbname - Database Operation"
        MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"

        ### Choice Menu Type, menu or radiolist
        __o_db_operation_menu_type_UI radiolist Select Re-retrieve Main-menu

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 operation ; read -u6 EXIT

        ### Re-retrieve Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_input_tac_id_UI updel

        ### Main-menu Button
        elif [ x"$exit_state" == x"3" ]
        then
            __o_db_operation_main_menu_UI

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -z "$operation" ] && __o_db_operation_menu_updel_UI

            case $operation in
                Update)
                    __o_db_operation_update_UI
                    ;;
                Delete)
                    __o_db_operation_delete_UI
                    ;;
            esac
        fi
    }

    ### Database Update Function
    __o_db_operation_update_UI() {
        field_info="$tmp_dir/__p_${tac_id}.update_field"
        field_form="$tmp_dir/__p_${tac_id}.update_form"
        dload_form="$tmp_dir/__d_${tac_id}.dialog_design_form_UI"

        update_sql="$tmp_dir/__p_${tac_id}.update_sql"
        update_msg="$tmp_dir/__p_${tac_id}.update_msg"

        sub_title="$dbname Database - Update Operation"
        MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"

        unset list_items ; declare -a list_items
        list_items=( `cat $product_info | awk -F':' '/PRODUCT_/ {print $1,"Field","off"}' | sed '/PRODUCT_CREATION_DATE/d ; /PRODUCT_ID/d'` )
        list_height=`echo ${list_items[*]} | xargs -n3 | wc -l`

        ### Call Dialog checklist UI
        __d_dialog_design_checklist_UI $field_info Select Re-Select Return $[list_height+9] 70 $list_height

        exit_state=$? ; #echo $exit_state ; exit

        update_field=( `cat $field_info` ) ; field_height=${#update_field[*]}
        field_max_width=`echo ${update_field[*]} | xargs -n1 | awk '{ if ( w < length($1) ) w = length($1) } END { print w }'`

        ### Re-Select Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_update_UI

        ### Return Button
        elif [ x"$exit_state" == x"3" ]
        then
            __o_db_operation_menu_updel_UI

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ x"$field_height" == x"0" ] && {
                MSG="\n Please select at least \Z1One\Zn field to Updated."
                __d_dialog_design_msgbox_UI Return 7 70
                __o_db_operation_update_UI
            }

            ### Load field form Data from Database Query
            __o_db_operation_update_load_form_UI
        fi
    }

    ### Database Update Function - Load form Data
    __o_db_operation_update_load_form_UI() {
        unset form_items ; declare -a form_items

        ### Append field form, the form look like this: [ label y x item y x flen ilen ] ...
        for key in ${!update_field[*]}
        do
            form_value=`awk -F': ' '/'"${update_field[$key]}"'/ {print $2}' $product_info`
            form_items[$key]="${update_field[$key]}: $[key+1] 1 '$form_value' $[key+1] $[field_max_width+3] 70 200"
        done

        MSG="\nEnter the following information:"

        set -- Update Re-input Return $[field_height+9] 70 $field_height
        ok_label=$1 ; cancel_label=$2 ; extra_label=$3 ; height=$4 ; width=$5 ; form_height=$6 ; shift 6

        ### Call Dialog form UI - load form data
        __d_dialog_design_form_UI ; sed -i '1,$ s/ $//' $dload_form ; source $dload_form

        exit_state=$? ; #echo $exit_state ; exit

        ### Re-input Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_update_load_form_UI

        ### Return Button
        elif [ x"$exit_state" == x"3" ]
        then
            rm -rf $field_info $field_form $dload_form
            __o_db_operation_update_UI

        ### Update Button
        elif [ x"$exit_state" == x"0" ]
        then
            ### Generate update DML statement
            awk 'ARGIND==1 { a[FNR]=$0 ; next } \
                 ARGIND==2 { print a[FNR]"='\''"$0"'\''" }' \
                 $field_info $field_form > $update_sql

            [ -f "$update_sql" ] && {
                sed -i '$!s/$/,/' $update_sql
                sed -i "1 s/^/update ${imgen_table[0]} set\n/" $update_sql
                sed -i "$ s/$/\nwhere PRODUCT_ID=$product_id;/" $update_sql
                sed -e '1,$ s/^/| /' -e '1,$ s/$/\\n/' $update_sql > $update_msg
            }

            msg_height=`sed -n '$=' $update_msg`

            MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
            MSG="$MSG\n\n------"
            MSG="$MSG\n\Z1WARNING:\Zn Are you sure want to perform \Z1Update\Zn Operation? ( Default: \Z1No\Zn )"
            MSG="$MSG\n\n+------"
            MSG="$MSG\n$(cat $update_msg)+------"

            ### Call Dialog yesno UI
            __d_dialog_design_yesno_UI Continue Cancel $[msg_height+13] 92

            exit_state=$? ; #echo $exit_state ; exit

            ### Cancel Button
            if [ x"$exit_state" == x"1" ]
            then
                __o_db_operation_update_UI

            ### Continue Button
            elif [ x"$exit_state" == x"0" ]
            then
                MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
                MSG="$MSG\n\nUpdate STATE :\n------"

                ### Call mysql Command, Update TAC_ID product informain ( Record - Row level, update imgen_product Table )
                __d_mysql_do_bin_cmd $dbname < $update_sql

                [ $? -eq 0 ] && update_state="Success" || update_state="Failure"
                MSG="$MSG\n       imgen_product \Z1Update\Zn   -   [\Z1 $update_state \Zn]"
                MSG="$MSG\n\nIf it was successful, please press the \Z1Re-retrieve\Zn button to Re-retrieve.\n"

                ### Call Dialog msgbox UI
                __d_dialog_design_msgbox_UI Re-retrieve 13 80

                exit_state=$? ; #echo $exit_state ; exit

                ### Re-retrieve Button
                if [ x"$exit_state" == x"0" ]
                then
                    __o_db_operation_input_tac_id_UI updel
                fi
            fi
        fi
    }

    ### Database Delete Function
    __o_db_operation_delete_UI() {
        delete_msg="$tmp_dir/__p_${tac_id}.delete_msg"

        sub_title="$dbname Database - Delete Operation"
        MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
        MSG="$MSG\n\n------"
        MSG="$MSG\n\Z1WARNING:\Zn Are you sure want to perform \Z1Delete\Zn Operation? ( Default: \Z1No\Zn )"
        MSG="$MSG\n\n+------"

        product_del_sql="delete from imgen_product where PRODUCT_ID=$product_id;"
        ref_del_sql="delete from imgen_ref where REF_ID=$ref_id;"
        tac_del_sql="delete from imgen_tac where TAC_REF=$tac_ref;"

        echo -e "$product_del_sql\n$ref_del_sql\n$tac_del_sql" > $delete_msg
        sed -i -e '1,$ s/^/| /' -e '1,$ s/$/\\n/' $delete_msg
        msg_height=`sed -n '$=' $delete_msg`

        MSG="$MSG\n$(cat $delete_msg)+------"

        ### Call Dialog yesno UI
        __d_dialog_design_yesno_UI Continue Cancel $[msg_height+13] 92

        exit_state=$? ; #echo $exit_state ; exit

        ### Cancel Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_menu_updel_UI

        ### Continue Button
        elif [ x"$exit_state" == x"0" ]
        then
            MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
            MSG="$MSG\n\nDelete STATE :\n------"

            unset delete_state ; declare -a delete_state

            ### Table: imgen_product ( 0 ) , imgen_ref ( 1 ) and imgen_tac ( 2 )
            for key in ${!imgen_table[*]}
            do
                case ${imgen_table[$key]} in
                    imgen_product)
                        query_condition="PRODUCT_ID=$product_id"
                        delete_sql="$product_del_sql"
                        ;;
                    imgen_ref)
                        query_condition="REF_ID=$ref_id"
                        delete_sql="$ref_del_sql"
                        ;;
                    imgen_tac)
                        query_condition="TAC_REF=$tac_ref"
                        delete_sql="$tac_del_sql"
                        ;;
                esac

                ### Call mysql Command, Delete TAC_ID product information ( Record - Row level )
                __d_mysql_do_bin_cmd $dbname -e "$delete_sql"

                [ $? -eq 0 ] && delete_state[$key]="Success" || delete_state[$key]="Failure"

                if [ x"$key" == x"0" ]
                then
                    MSG="$MSG\n       ${imgen_table[$key]} \Z1Delete\Zn   -   [\Z1 ${delete_state[$key]} \Zn]"

                elif [ x"$key" == x"1" -o x"$key" == x"2" ]
                then
                    MSG="$MSG\n       ${imgen_table[$key]}     \Z1Delete\Zn   -   [\Z1 ${delete_state[$key]} \Zn]"
                fi
            done

            MSG="$MSG\n\nIf it was successful, please press the \Z1Re-retrieve\Zn button to Re-retrieve.\n"

            ### Call Dialog msgbox UI
            __d_dialog_design_msgbox_UI Re-retrieve 15 80

            exit_state=$? ; #echo $exit_state ; exit

            ### Re-retrieve Button
            if [ x"$exit_state" == x"0" ]
            then
                __o_db_operation_input_tac_id_UI updel
            fi
        fi
    }

    ### Database Backup Function
    __o_db_operation_backup_UI() {
        tac_id_array=( `echo $tac_id | awk -v FS=',' '{ $1=$1 ; print $0 }' | xargs -n1 | awk '!a[$0]++ { print $0 }'` )

        for tac_id in ${tac_id_array[*]}
        do
            product_info="$tmp_dir/__p_${tac_id}.product_info"

            ### Retrieve TAC_ID product informatin
            __o_db_operation_query_tac_id_info $product_info normal

            if [ -z "$tac_id" -o -z "$product_id" -o -z "$ref_id" ]
            then
                MSG="\nUnable to retrieve TAC_ID (\Z1 $tac_id \Zn) Product information."
                __d_dialog_design_msgbox_UI Return 7 70
                __o_db_operation_input_tac_id_UI backup
            fi
        done

        level_info="$tmp_dir/__0_${pid}.backup_level"
        MSG="\n- Note: \Z1Restore\Zn operation Provide \Z1Record-level ( Row )\Zn recovery -"

        unset list_items ; declare -a list_items

        ### Backup level: db_level ( 0 ), table_level ( 1 ) and record_level ( 2 )
        for key in ${!backup_level[*]}
        do
            case ${backup_level[$key]} in
                db_level)
                    list_items[$key]="Database Level off"
                    ;;
                table_level)
                    list_items[$key]="Table Level off"
                    ;;
                record_level)
                    list_items[$key]="Record Level on"
                    ;;
            esac
        done

        list_height=${#list_items[*]}

        ### Call Dialog checklist UI
        __d_dialog_design_checklist_UI $level_info Select Re-Select Main-menu $[list_height+9] 70 $list_height

        exit_state=$? ; #echo $exit_state ; exit

        select_level=( `cat $level_info` ) ; level_count=${#select_level[*]}

        ### Re-Select Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_backup_UI

        ### Main-menu Button
        elif [ x"$exit_state" == x"3" ]
        then
            __o_db_operation_main_menu_UI

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ x"$level_count" == x"0" ] && {
                MSG="\n Please select at least \Z1One\Zn Backup level."
                __d_dialog_design_msgbox_UI Return 7 70
                __o_db_operation_backup_UI
            }

            echo ${select_level[*]} | grep -qE "Database" ; [ $? -eq 0 ] && is_db_level=true || is_db_level=false
            echo ${select_level[*]} | grep -qE "Table" ; [ $? -eq 0 ] && is_table_level=true || is_table_level=false
            echo ${select_level[*]} | grep -qE "Record" ; [ $? -eq 0 ] && is_record_level=true || is_record_level=false

            date_now=`date +%Y%m%d%H%M.%S`

            [ x"$is_db_level" == x"true" ] && mkdir -p $db_level_dir/$date_now
            [ x"$is_table_level" == x"true" ] && mkdir -p $table_level_dir/$date_now

            MSG="\nBackup DIR :\n------"
            MSG="$MSG\n      \Z1 Database \Zn -  [ $backup_dir/db_level_dir/$date_now ]"
            MSG="$MSG\n      \Z1 Table    \Zn -  [ $backup_dir/table_level_dir/$date_now ]"
            MSG="$MSG\n      \Z1 Record   \Zn -  [ $backup_dir/record_level_dir/TAC_ID/$date_now ]"
            MSG="$MSG\n\nBackup STATE :\n------"

            ### Call mysqldump Command, Backup IMGEN Database.
            [ x"$is_db_level" == x"true" ] && {
                __d_mysql_do_dump_cmd db_level $dbname | gzip > $db_level_dir/$date_now/${dbname}.sql.gz

                [ $? -eq 0 ] && backup_state="Success" || backup_state="Failure"
                MSG="$MSG\n       Database Level   -   [\Z1 $backup_state \Zn]"
            }

            ### Call mysqldump Command, Backup Table Data ( include imgen_product, imgen_ref and imgen_tac ).
            [ x"$is_table_level" == x"true" ] && {
                __d_mysql_do_dump_cmd table_level $dbname ${imgen_table[*]} | gzip > $table_level_dir/$date_now/${dbname}_table.sql.gz

                [ $? -eq 0 ] && backup_state="Success" || backup_state="Failure"
                MSG="$MSG\n       Table    Level   -   [\Z1 $backup_state \Zn]"
            }

            ### Call mysql Command, export TAC_ID product information ( Record - Row level, include insert and update DML statement ).
            [ x"$is_record_level" == x"true" ] && {
                for tac_id in ${tac_id_array[*]}
                do
                    product_info="$tmp_dir/__p_${tac_id}.product_info"

                    ### Retrieve TAC_ID product informatin
                    __o_db_operation_query_tac_id_info $product_info normal

                    if [ -z "$tac_id" -o -z "$product_id" -o -z "$ref_id" ]
                    then
                        MSG="\nUnable to retrieve TAC_ID (\Z1 $tac_id \Zn) Product information."
                        __d_dialog_design_msgbox_UI Re-retrieve 7 70
                        __o_db_operation_input_tac_id_UI backup
                    fi

                    product_id_array=( `echo $product_id` )
                    tac_ref_array=( `echo $tac_ref` )
                    ref_id_array=( `echo $ref_id` )

                    if [ "${#product_id_array[*]}" -gt 1 -o "${#tac_ref_array[*]}" -gt 1 -o "${#ref_id_array[*]}" -gt 1 ]
                    then
                        MSG="$MSG\n       Record   Level   -   [\Z1 Failure - $tac_id / Conflict\Zn ]"

                    else
                        record_level_dir="$backup_dir/record_level/$tac_id/$date_now" ; mkdir -p $record_level_dir

                        ### Generate insert DML statement
                        insert_sql() {
                            table=$1 ; history_data=$2 ; shift 2

                            extra_comma_row=$[$(sed -n '$=' $history_data)+1]

                            awk -F': ' 'BEGIN { print "insert into '$table' (" } \
                                        { a[NR]=$2 ; b[j++]=NR ; print $1","} \
                                        END { print ") values (" ; for (i=0;i<j;i++) print "'\''"a[b[i]]"'\''," }' $history_data | \
                                        sed -e '1,$ s/^ \{1,\}// ; $s/,/ );/' -e ''${extra_comma_row}' s/,$//'
                        }

                        ### Generate update DML statement
                        update_sql() {
                            table=$1 ; history_data=$2 ; shift 2

                            awk -F': ' '{print $1"='\''"$2"'\''"}' $history_data | \
                                        sed '1,$ s/^ \{1,\}//; $! s/$/,/' | \
                                        sed "1 s/^/update ${imgen_table[$key]} set\n/" | \
                                        sed "$ a where $query_condition;"
                        }

                        unset backup_state ; declare -a backup_state

                        ### Table: imgen_product ( 0 ), imgen_ref ( 1 ) and imgen_tac ( 2 )
                        for key in ${!imgen_table[*]}
                        do
                            case ${imgen_table[$key]} in
                                imgen_product)
                                    query_condition="PRODUCT_ID=$product_id"
                                    ;;
                                imgen_ref)
                                    query_condition="REF_ID=$ref_id"
                                    ;;
                                imgen_tac)
                                    query_condition="TAC_REF=$tac_ref"
                                    ;;
                            esac

                            history_data="$record_level_dir/${dbname}.${imgen_table[$key]}.history_data"
                            insert_sql_file="$record_level_dir/${dbname}.${imgen_table[$key]}.sql_insert"
                            update_sql_file="$record_level_dir/${dbname}.${imgen_table[$key]}.sql_update"

                            ### Call mysql Command, generate history Backup Query Data
                            __d_mysql_do_bin_cmd $dbname -e "select * from ${imgen_table[$key]} where ${query_condition}\G" | sed '1d' > $history_data

                            [ $? -eq 0 ] && backup_state[$key]="Success" || backup_state[$key]="Failure"

                            [ "${imgen_table[$key]}" == "imgen_product" ] && {
                                sed -i -e '/PRODUCT_CODE/ { h;d } ; /PRODUCT_REF_COM/ { G }' $history_data
                            }

                            ### Generate insert and update DML statement
                            insert_sql ${imgen_table[$key]} $history_data > $insert_sql_file
                            update_sql ${imgen_table[$key]} $history_data > $update_sql_file
                        done

                        echo ${backup_state[*]} | grep -q 'Failure' ; [ $? -eq 0 ] && backup_state="Failure" || backup_state="Success"
                        MSG="$MSG\n       Record   Level   -   [\Z1 $backup_state - $tac_id \Zn]"
                    fi
                done
            }

            __d_dialog_design_msgbox_UI Return $[level_count + ${#tac_id_array[*]} + 13] 80
            __o_db_operation_main_menu_UI
        fi
    }

    ### Database Restore Function - Retrieve TAC_ID
    __o_db_operation_restore_UI() {
        record_level_dir="$backup_dir/record_level" ; mkdir -p $record_level_dir

        sub_title="$dbname Database - Restore Operation" ; width=70

        unset list_items ; declare -a list_items

        list_items=( `find $record_level_dir -maxdepth 1 | sed '1d' | awk -F'/' '{print $NF}' | sort` )
        list_height=${#list_items[*]}

        [ x"$list_height" == x"0" ] && {
            MSG="\nThe history Backup Point of TAC_ID \Z1Not found\Zn."
            __d_dialog_design_msgbox_UI Return 7 70
            __o_db_operation_main_menu_UI
        }

        list_items=( `echo ${list_items[*]} | xargs -n1 | awk '{print $1,"TAC_ID","off"}'` )

        MSG="- Select Backup (\Z1 TAC_ID \Zn) to Restore -"
        MSG=`echo $MSG | sed ':a s/^.\{1,'"$width"'\}$/ & /;ta'`
        MSG="\n$MSG"

        unset product_id tac_id tac_fac tac_ref ref_id product_id_array tac_id_array tac_ref_array

        ### Call Dialog radiolist UI
        __d_dialog_design_radiolist_UI Select Re-select Main-menu $[list_height+9] $width $list_height

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 tac_id ; read -u6 EXIT

        ### Re-select Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_restore_UI

        ### Main-menu Button
        elif [ x"$exit_state" == x"3" ]
        then
            __o_db_operation_main_menu_UI

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -z "$tac_id" ] && __o_db_operation_restore_UI
            __o_db_operation_restore_tac_id_UI
        fi
    }

    ### Database Restore Function - Restore TAC_ID
    __o_db_operation_restore_tac_id_UI() {
        MSG1="\n- Note: \Z1Restore\Zn operation Provide \Z1Record-level ( Row )\Zn recovery -"
        MSG2="| TAC_ID: \Z1$tac_id\Zn |"
        width=70
        MSG1=`echo $MSG1 | sed ':a s/^.\{1,'"$[width-1]"'\}$/ & /;ta'`
        MSG2=`echo $MSG2 | sed ':a s/^.\{1,'"$[width-1]"'\}$/ & /;ta'`
        MSG="$MSG1\n$MSG2"

        record_point="$record_level_dir/$tac_id"

        ### Retrieve TAC_ID Backup Number
        history_point=( `find $record_point -maxdepth 1 | sed '1d' | awk -F'/' '{print $NF}' | sort` )
        list_height=${#history_point[*]}

        [ x"$list_height" == x"0" ] && {
            MSG="\nThe TAC_ID (\Z1 $tac_id \Zn) history Backup Point \Z1Not found\Zn."
            __d_dialog_design_msgbox_UI Return 7 70
            __o_db_operation_main_menu_UI
        }

        unset list_items ; declare -a list_items

        ### Retrieve TAC_ID Backup level
        for p_key in ${!history_point[*]}
        do
            unset exist_level ; declare -a exist_level

            ### Backup level: db_level ( 0 ) , table_level ( 1 ) and record_level ( 2 )
            for l_key in ${!backup_level[*]}
            do
                case ${backup_level[$l_key]} in
                    db_level)
                        history_bkdir="$db_level_dir/${history_point[$p_key]}"
                        [ `ls $history_bkdir | wc -l` -ne 0 ] && exist_level[$l_key]=D
                        ;;
                    table_level)
                        history_bkdir="$table_level_dir/${history_point[$p_key]}"
                        [ `ls $history_bkdir | wc -l` -ne 0 ] && exist_level[$l_key]=T
                        ;;
                    record_level)
                        history_bkdir="$record_point/${history_point[$p_key]}"
                        [ `ls $history_bkdir | wc -l` -ne 0 ] && exist_level[$l_key]=R
                        ;;
                esac
            done

            exist_level=`echo ${exist_level[*]} | awk -v OFS='-' '{ $1=$1 ; print $0 }'`
            list_items[$p_key]=`echo ${history_point[$p_key]} | xargs -n1 | awk -F'/' '{print $NF,"'$exist_level'","off"}'`
        done

        ### Call Dialog radiolist UI
        __d_dialog_design_radiolist_UI Select Re-select Return $[list_height+10] 70 $list_height

        exit_state=$? ; echo -e "\nexit" >&6
        read -u6 restore_point ; read -u6 EXIT

        ### Re-select Button
        if [ x"$exit_state" == x"1" ]
        then
            __o_db_operation_restore_tac_id_UI

        ### Return Button
        elif [ x"$exit_state" == x"3" ]
        then
            __o_db_operation_restore_UI

        ### Select Button
        elif [ x"$exit_state" == x"0" ]
        then
            [ -z "$restore_point" ] && __o_db_operation_restore_tac_id_UI

            restore_msg="$tmp_dir/__p_${tac_id}.restore_msg"
            restore_bkdir="$record_point/$restore_point"
            record_level="$restore_bkdir/$dbname"

            ### Retrieve TAC_ID history Backup Point ( Record - Row level )
            [ `ls ${record_level%/$dbname} | wc -l` -eq 0 ] && {
                MSG="\nThe history Backup Point dose not have \Z1Record-level ( Row )\Zn backup.\n"

                ### Call Dialog msgbox UI
                __d_dialog_design_msgbox_UI Return 7 80

                exit_state=$? ; #echo $exit_state ; exit

                ### Return Button
                if [ x"$exit_state" == x"0" ]
                then
                    __o_db_operation_restore_tac_id_UI
                fi
            }

            ### Display TAC_ID history Backup Point Query Data
            result_file=`echo ${imgen_table[*]} | xargs -n1 | \
                         awk '{print "'$record_level'."$1".history_data"}' | xargs`

            field_max_width=`awk -F': ' '{ if ( w < length($1) ) w=length($1) } END { print w } ' $result_file`
            sed -i ':a s/^.\{1,'"$[field_max_width-1]"'\}:.*$/ &/;ta' $result_file
            tac_point="- TAC_ID: $tac_id | Restore Point: $restore_point -"
            tac_point=`echo $tac_point | sed ':a s/^.\{1,82\}$/ & /;ta'`
            echo -e "\n $tac_point \n" > $restore_msg ; more $result_file >> $restore_msg

            ### Call Dialog yesno UI
            __d_dialog_design_textbox_UI $restore_msg Next 30 90

            exit_state=$? ; #echo $exit_state ; exit

            ### Next Button
            if [ x"$exit_state" == x"0" ]
            then
                ### Table: imgen_product ( 0 ), imgen_ref ( 1 ) and imgen_tac ( 2 )
                product_id=`awk '/PRODUCT_ID/ {print $2}' ${record_level}.${imgen_table[0]}.history_data`
                ref_id=`awk '/REF_ID/ {print $2}' ${record_level}.${imgen_table[1]}.history_data`
                tac_ref=`awk '/TAC_REF/ {print $2}' ${record_level}.${imgen_table[2]}.history_data`

                product_info="$tmp_dir/__p_${tac_id}.product_info"

                ### Retrieve TAC_ID product informatin
                __o_db_operation_query_tac_id_info $product_info normal

                MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
                MSG="$MSG\n\n------"
                MSG="$MSG\n\Z1WARNING:\Zn Are you sure want to perform \Z1Restore\Zn Operation? ( Default: \Z1No\Zn )\n"
                MSG="$MSG\nIf the Product information is exists, the \Z1Update\Zn statement will be executed."
                MSG="$MSG\nIf not, the \Z1Insert\Zn statement will be executed."
                MSG="$MSG\n\n+------"

                ### Display history Backup Point insert or update DML statement file
                if [ x"$fline_count" == x"1" ]
                then
                    echo ${imgen_table[*]} | xargs -n1 | awk '{print "| '$record_level'."$1".sql_insert"}' > $restore_msg
                else
                    echo ${imgen_table[*]} | xargs -n1 | awk '{print "| '$record_level'."$1".sql_update"}' > $restore_msg
                fi

                sed -i -e '1,$ s/$/\\n/' $restore_msg
                msg_height=`sed -n '$=' $restore_msg`

                MSG="$MSG\n$(cat $restore_msg)+------"

                ### Call Dialog yesno UI
                __d_dialog_design_yesno_UI Continue Cancel $[msg_height+16] 92

                exit_state=$? ; #echo $exit_state ; exit

                ### Cancel Button
                if [ x"$exit_state" == x"1" ]
                then
                    __o_db_operation_restore_UI

                ### Continue Button
                elif [ x"$exit_state" == x"0" ]
                then
                    MSG="\nTAC_ID: \Z1$tac_id\Zn | PRODUCT_ID: \Z1$product_id\Zn | TAC_REF: \Z1$tac_ref\Zn | REF_ID: \Z1$ref_id\Zn"
                    MSG="$MSG\n\nRestore STATE :\n------"

                    unset restore_state ; declare -a restore_state

                    ### Table: imgen_product ( 0 ), imgen_ref ( 1 ) and imgen_tac ( 2 )
                    for key in ${!imgen_table[*]}
                    do
                        if [ x"$fline_count" == x"1" ]
                        then
                            restore_sql="${record_level}.${imgen_table[$key]}.sql_insert"
                        else
                            restore_sql="${record_level}.${imgen_table[$key]}.sql_update"
                        fi

                        ### Call mysql Command, Restore TAC_ID product informatin ( Record - Row level )
                        __d_mysql_do_bin_cmd $dbname < $restore_sql

                        [ $? -eq 0 ] && restore_state[$key]="Success" || restore_state[$key]="Failure"

                        if [ x"$key" == x"0" ]
                        then
                            MSG="$MSG\n       ${imgen_table[$key]} \Z1Restore\Zn   -   [\Z1 ${restore_state[$key]} \Zn]"

                        elif [ x"$key" == x"1" -o x"$key" == x"2" ]
                        then
                            MSG="$MSG\n       ${imgen_table[$key]}     \Z1Restore\Zn   -   [\Z1 ${restore_state[$key]} \Zn]"
                        fi
                    done

                    MSG="$MSG\n\nIf it was successful, please press the \Z1Re-retrieve\Zn button to Re-retrieve.\n"

                    ### Call Dialog msgbox UI
                    __d_dialog_design_msgbox_UI Re-retrieve 15 80

                    exit_state=$? ; #echo $exit_state ; exit

                    ### Re-retrieve Button
                    if [ x"$exit_state" == x"0" ]
                    then
                        __o_db_operation_input_tac_id_UI updel
                    fi
                fi
            fi
        fi
    }

    ### __o_db_operation_UI Function - Main Execute Entry
    __o_db_operation_connect_UI
}

#------------
# Main
#------

__d_dialog_design_UI
__o_db_operation_UI

原创文章,作者:影·随行,如若转载,请注明出处:http://www.178linux.com/8976

评论列表(5条)

  • stanley
    stanley 2015-10-27 23:26

    呵!胆大心细,总结出来也一定有不小的收获吧。总结的也非常棒,果断置顶

    • 影·随行
      影·随行 2015-10-28 09:43

      @stanley最早写了一版 CentOS 5.5 全自动的,后面因为生产环境上开发环境不一致,所以到 6.X 直接改成半自动化了~~整个脚本加测试,调试UI输出用了整整一个月,-_## 现在不怎么改这个脚本了,越改越多,不敢改了,而且现在没必要用这种脚本来部署其它环境了,直接用 ansilbe/saltstack 就行了
      哈,数据库那个,是线上的一个小系统,不过数据是挺重要的,他们设计那种BUG都能出现,而且那个手工去查、修正,太费时了,时间要用在点上,我不关心业务系统,只关心数据库,他们有输出文档,知道怎么回事,就用 shell 直接实现了,业务系统有其它人维护,这种重复性工作还是算了~~不case,测试加写脚本用了一个星期才完成了,脚本用得很好,没问题哈哈

    • stanley
      stanley 2015-10-31 21:36

      @影·随行赞 坚持

  • jemmy
    jemmy 2015-10-30 11:05

    膜拜中!!!!

  • wujunlin
    wujunlin 2016-02-28 22:25

    真厉害啊