29 October 2009
Version: 1.0
Revision History
- Draft 1 – 26 October 2009
- Draft 2 – 29 October 2009
- Log files will have a default extension of .log See section 2.3 for updated information on accessible convenience defaults
- Current scripts have been changed to use LOG_EXT and DEFAULT_LOG_FILE where necessary
- Additional detailed information of logging output and examples in new section 3.4.1 including practices for detecting alerting conditions.
- Added additional information on common.sh and details of file within version control. See section 3.3.4
- Refactored code and added documentation on stopping scripts with STOP_FILE. See Section 4.1
- Added standards for MySQL authentication. See Section 4.2
- Updated other typographical errors
1. Overview
This document describes the standards used for all new written scripts managed under version control for the MySQL DBA group.
By default, all DBA scripts should be written in shell programming language (/bin/sh).
2. File Standards
2.1 Projects
A project is defined as a set of files that perform a specific purpose.
Projects enable a means of packaging via the Release Management system
Projects enable the included files to be released at various different release schedules independently of other projects.
The default projects at this time as specified in Version Control are:
- admin – Administration scripts used to create and manage systems
- operations – DBA support scripts for normal DBA operations
- monitoring – Specific DBA monitoring product support
2.2 Directory Standards
Within each project the following default top level directories are in place:
/etc
/doc
/log
The definition of each directory is:
- /scripts holds all scripts that are written for this project
- /etc holds all configuration files that are used for this project, by default a configuration file should exist for each script
- /doc holds all supporting documentation for the scripts or other programs in this project
- /log holds by default an log files produced from this project. This directory is to not contain any files under version control, the directory exists only for minimum deployment requirements.
It is possible that other directories exist for a given file type as determined for the project. For example:
- /sql holds all SQL scripts
2.3 File Naming Standards
File names used for scripts, configuration, logs and other files within projects are to follow the following file naming standards:
- All Shell scripts are to have the extension .sh
- All Configuration files are to have the extension .cnf
- Log files are to have the default extension .log
- If a script produces an output file of a specific format, the extension should reflect the file format, e.g. .csv, .tsv etc.
- All filenames are to be in lowercase only.
- The underscore ‘_’ character is to be used as a word boundary.
- All scripts are to be defined with a name that adequately describes the script without need to investigate further the purpose of the script.
- Filenames may include letters and numbers in addition to underscore ‘_’.
- Spaces are never allowed in any filenames.
- The period ‘.’ is to only be used for extensions, or for specific sub-categories of information within the filename.
- Configuration files when designed to be customized per server, are to have the version control file suffixed with .sample to ensure they do not overwrite any deployed scripts
The following are examples of filenames in use:
- checklist.sh
- checklist.mysql.cnf, checklist.system.cnf
- ip_poller.sh
These are INVALID filenames:
- ESM_monitoring.sh
- my_sample_script
- CamelCaseNames.sh
2.3 Log File Standards
Log files are always to include a date/time stamp that enable a list of log files for the given type of script in a chronological order. This is defined with the following common system variable that is specified at the start of all scripts.
A log file name must include the following components.
- Script name (i.e. SCRIPT_NAME)
- Execution Date/Time (i.e. DATE_TIME)
- .log extension, defined as LOG_EXT
There is no specific format for log files, however it is recommended you following the following format:
If you wish to use this format, you simply need to:
LOG_FILE=${DEFAULT_LOG_FILE}
3. Scripting Standards
3.1 Script Layout
All scripts are to be written to support the default bourne shell. All scripts are to commence with the following first line.
All scripts are to have as the last line the following comment.
- Other then variable declarations and the call to main, all scripting code is to be defined within functions.
- The default indentation is 2 spaces.
- Tab characters are not allowed for formatting indentation.
- All content within scripts are to be in English only, including names, variables and comments.
3.2 Variable Coding Standards
Variables are used thought scripts. These should use the following requirements:
- All variables are to be in UPPERCASE
- Within functions, local is be used to restrict the scope of variables
3.2.1 Mandatory Script Variables
All scripts must define the following variables as part of the Script Definition section at the top of the script.
SCRIPT_VERSION=”0.01 DD-MMM-YYYY $Id$”
SCRIPT_REVISION=”$Revision$”
The script version includes a human managed version number and date for readability within the –version and –help options.
NOTE: $Id$ and $Revision$ are standard RCS tags that the current version control system supports. See http://kb.perforce.com/UserTasks/ManagingFile..Changelists/UsingRcsKeywords for more information.
3.2.2 Global Script Variables
A number of pre-defined global variables are used and available to all scripts:
Directories
- BASE_DIR This is the base directory of the project that can be used to reference other files
- SCRIPT_DIR This is the /scripts project directory
- CNF_DIR This is the /etc project directory
- LOG_DIR This is the /log project directory
- TMP_DIR This is a temporary working directory, currently this defaults to /tmp
When creating global variables for important paths, allow for override of these by the controlling shell environment. For example.
TMP_DIR=”/tmp” # Incorrect defintion
Files
- TMP_FILE A pre-defined unique temporary file that is auto removed on completion
- STOP_FILE A pre-defined file to stop script processing in loops (only if used in functions)
- DEFAULT_CNF_FILE A pre-defined standard /etc config file name
- DEFAULT_LOG_FILE A pre-defined standard /log log file name
Variables
- DATE_TIME – The date/time of the script execution
- DATE_TIME_TZ – The date/time/timezone of the script execution
- USER_ID – The running user id
- FULL_HOSTNAME – The full and qualified hostname
- SHORT_HOSTNAME – The short hostname
- LOG_DATE_FORMAT – The Date Format used for all log files
Other Variables
- QUIET – Quiet Logging, ERROR and WARN only
- USE_DEBUG – Enable Debugging
3.2.3 Variable Usages
When using variables, they are always to be enclosed in curly brackets.
- ${TMP_FILE} is acceptable
- $TMP_FILE is NOT acceptable
When displaying variables in stdout, they should always be included in single quotes (‘) to ensure actual value can be determined.
- info “Exiting with status code of ‘${EXIT_CODE}'”
3.2.4 Unacceptable practices
- Hardcoding values into scripts that are configuration values, e.g. email distribution list values
- Using hardcoded constants when a function or command can be used to obtain the value, e.g. hostname
- Repeating common strings of combined values, these are to be refactored into one variable
echo “start” > ${LOG_FILE}/${SCRIPT_NAME}…${DATE_TIME+.txt”
Should be rewritten as:
echo “The log file is ${LOG_FILE}”
echo “start” > ${LOG_FILE}
3.3 Function Coding Standards
All code is to be written within a self contained function. The purpose is to allow for the testability and re-use of these functions.
The following standards apply to individual functions within all scripts:
- All functions are to be prefixed with a minimum 3 line header comment
- The first line is 80 characters long, and the function name is right aligned surrounded with a single space and two (2) trailing dashes ‘–‘
- The second line is a one line short summary of the functions purpose
- The third line is a single comment ‘#’ to separate the comments from the function code
- The function name is to preceed on the following line and include the suffix of () {
- All function names are to be in lower case, and use a underscore ‘_’ as a word boundary.
- A single blank line is to separate functions
- A function should always return an integer value
3.3.1 Function Coding Example
# Display Script help syntax
#
help() {
…
return 0
}
There is no main function within a shell script. In this instance, all scripts will define a main() function, and this is the only execution entry point. This is to be the last physical executable line of code of the script
# Main Script Processing
#
main() {
…
return 0
}
3.3.2 Mandatory Script Functions
All scripts are to have the following default functions:
- bootstrap
- help
- process_args
- main
bootstrap
The bootstrap function is to be identical in all scripts, this is used to source necessary common functions used by all scripts. The current format of this function is, and must be included physically in all scripts:
# Essential script bootstrap
#
bootstrap() {
DIRNAME=`dirname $0`
COMMON_SCRIPT_FILE=”${DIRNAME}/common.sh”
[ ! -f “${COMMON_SCRIPT_FILE}” ] && echo “ERROR: You must have a matching ‘${COMMON_SCRIPT_FILE}’ with this script ${0}” && exit 1
. ${COMMON_SCRIPT_FILE}
set_base_paths
return 0
}
The function performs the following operations.
- Sources the necessary companion common.sh script
- Defines the BASE_DIR variable if not defined by the shell. Used by all scripts
- Defines the LOG_DIR variable if not defined by the shell
- Defines the TMP_DIR variable if not defined by the shell
- Defines the internal CNF_DIR and SCRIPT_DIR variables
help
The help function is to display the usage of the function and then exit. The usage needs to specific all command line arguments, and client identify mandatory and optional arguments. See the example.sh below for the syntax of the help function.
process_args
This function is used to process the command line arguments for scripts. The getopt function is used for processing arguments however this only support single character options (e.g. -v -h -p etc). Scripts should be written for only single character options.
To improve the namespace, as well as provide a difference between operational parameters and information parameters the following two word options are used.
- –help Display script help and exit
- –version Display single line script version and exit
These options are managed by a common.sh function that should be executed as the first line in the process_args functions.
check_for_long_args $*
See the example.sh for a working example the syntax for this function.
main
The main function is to include at minimum the following code:
# Main Script Processing
#
main () {
[ ! -z “${TEST_FRAMEWORK}” ] && return 1
bootstrap
process_args $*
commence
complete
return 0
}
main $*
# END
3.3.3 Function examples
Each function should perform the following in the defined order.
- Check for required number of arguments
- Assign local variables to arguments
- Check for individual arguments
- provide debugging or information output on operation
- perform operation
- return success
For example.
[ $# -ne 1 ] && fatal “sample() This function requires one argument.”
local VAL=$1
[ -z “${VAL}” ] && fatal “sample() $VAL is not defined”
info “Running sample function with ‘${VAL}'”
# DO WORK HERE
return 0
}
3.3.4 Common Functions
A small set of common functions are defined in the common.sh script. This is required for all scripts and is included as part of the bootstrap function.
The current list as at version 0.03 is:
- set_base_paths – This function sets the base directory variables BASE_DIR,CNF_DIR,SCRIPT_DIR as well as various DEFAULT files
- version - This function returns script standard version information and exits.
- check_for_long_args – This function pre checks for –help and –version options
- commence – This function writes a starting script INFO line and starts timing
- complete – This function writes a ending script INFO line and shows total execution time
- check_stop_file – This function exists a long running script nicely if STOP_FILE exists.
- fatal – This function logs a FATAL message in the standard format and exits
- error - This function logs a ERROR message in the standard format and exits
- warn - This function logs a WARN message in the standard format
- info - This function logs a INFO message in the standard format if not in quiet mode
- debug - This function logs a DEBUG message in the standard format if debug is enabled
- log - This function performs the low level standard logging
- cleanup_exit - This function logs a message base on the edit code, cleans up any temporary files and exits
Refer to Version control /admin/scripts/common.sh for a current version of this script.
3.4 Output Display Standards
Standard output is never to be sent using the echo command. The defined function info is used to provide a consistent and reproducible output.
There are 5 different level of output these are:
- fatal – Used for internal misconfigurations only. Reported as FATAL
- error – Used to indicate a terminating error of the script. The script will stop processing. Reported as ERROR
- warn – Used to indicate a warning that should be investigated. Reported as WARN
- info – Used to display valuable information. Reported as INFO
- debug – Used for debugging purposes. Reported as DEBUG
All output from these functions is sent to stdout. By default, scripts should not redirect default script output to a log file. This should be determined on a case by case basis and also the encapsulation of any script within other scripts.
3.4.1 Output Display Examples
- For production operation running in quiet mode with the -q option will only display errors and warnings, this will suppress INFO messages. By default, your script should not produce warnings or errors, so a non empty output in quiet mode can be determine as an unsuccessful execution. You can use this condition in script management.
- Debugging output is only shown if the -v option is provided to a script, and this in turn enables the USE_DEBUG environment variable. This variable can also be set by the calling environment or shell. This output can be verbose and is only for debugging purposes. Production scripts should never be run in debug. If additional information is required, consider adding additional info calls.
- Date & Time & Timezone
- Output Level (FATAL,ERROR,WARN,INFO,DEBUG)
- Script name
- Message
- YYYYMMDD HH:MM:SS -TZTZ XXXXX [scriptname]
$ ./example.sh -X x
20091029 13:24:54 -0400 INFO [example] Script started (Version: 0.03 DD-MMM-YYYY $)
20091029 13:24:54 -0400 INFO [example] Script completed successfully (0 secs)
./example.sh -X x -q
The benefit of quiet mode is that a successful script should return a zero exit code and have no stdout. Either of these conditions can trigger an alerting process.
$ ./example.sh
20091029 13:27:07 -0400 ERROR [example] You must specify a sample value for -X. See –help for full instructions.
20091029 13:27:07 -0400 INFO [example] Exiting with status code of ‘1’
$ echo $?
1
3.5 Script Template
The following example.sh as defined in version control acts as the current template for all new scripts
3.5.1 example.sh
#
#——————————————————————————-
# Name: example.sh
# Purpose: Example Shell Script with minimum requirements
# Author: Ronald Bradford http://ronaldbradford.com/mysql-dba
#——————————————————————————-
#——————————————————————————-
# Script Definition
#
SCRIPT_NAME=`basename $0 | sed -e “s/.sh$//”`
SCRIPT_VERSION=”0.01 DD-MMM-YYYY $Id$”
SCRIPT_REVISION=”$Revision”
#——————————————————————————-
# Constants – These values never change
#
#——————————————————————————-
# Script specific variables
#
#—————————————————————– bootstrap –
# Essential script bootstrap
#
bootstrap() {
DIRNAME=`dirname $0`
COMMON_SCRIPT_FILE=”${DIRNAME}/common.sh”
[ ! -f “${COMMON_SCRIPT_FILE}” ] && echo “ERROR: You must have a matching ‘${COMMON_SCRIPT_FILE}’ with this script ${0}” && exit 1
. ${COMMON_SCRIPT_FILE}
set_base_paths
return 0
}
#———————————————————————– help –
# Display Script help syntax
#
help() {
echo “”
echo “Usage: ${SCRIPT_NAME}.sh -X <example-string> [ -q | -v | –help | –version ]”
echo “”
echo ” Required:”
echo ” -X Example mandatory parameter”
echo “”
echo ” Optional:”
echo ” -q Quiet Mode”
echo ” -v Verbose logging”
echo ” –help Script help”
echo ” –version Script version (${SCRIPT_VERSION}) ${SCRIPT_REVISION}”
echo “
return 0
}
#————————————————————– process_args –
# Process Command Line Arguments
#
process_args() {
check_for_long_args $*
debug “Processing supplied arguments ‘$*'”
while getopts X:qv OPTION
do
case “$OPTION” in
X) EXAMPLE_ARG=${OPTARG};;
q) QUIET=”Y”;;
v) USE_DEBUG=”Y”;;
esac
done
shift `expr ${OPTIND} – 1`
[ -z “${EXAMPLE_ARG}” ] && error “You must specify a sample value for -X. See –help for full instructions.”
return 0
}
#———————————————————————– main –
# Main Script Processing
#
main () {
[ ! -z “${TEST_FRAMEWORK}” ] && return 1
bootstrap
process_args $*
commence
complete
}
main $*
# END
3.5.2 example.sh Output
Default invocation
20090922 10:44:29 -0700 ERROR [example] You must specify a sample value for -X. See –help for full instructions.
20090922 10:44:29 -0700 INFO [example] Exiting with status code of ‘1’
Help with –help
Usage: example.sh -X <example-string> [ -v | –help | –version ]
Required:
-X Example Mandatory parameter
Optional:
-v Vebose logging
–help Script help
–version Script version 0.01 DD-MM-YYYY
Version with –version
Name: example.sh Version: 0.01 DD-MMM-YYYY
Correct invocation (using mandatory argument)
20090922 10:44:35 -0700 INFO [example] Script started (Version: 0.01 DD-MMM-YYYY)
20090922 10:44:35 -0700 INFO [example] Script completed successfully (0 secs)
3.5.2 example.sh Usage
This is to be used when creating any new scripts. If the example.sh script is modified, the change should be manually reflected in ALL existing scripts at the time of the change to ensure a level of consistency of all scripts.
4. General Standards
4.1 Long running scripts
$ admin/scripts/run_cmd.sh -h admin/etc/servers.mysql.txt -c hostname
20091029 19:47:29 +0000 INFO [run_cmd] Script started (Version: 0.02 28-OCT-2009 $)
20091029 19:47:29 +0000 INFO [run_cmd] Log file for host output can be found in ‘admin//log/run_cmd.20091029.1947.nsh1.log’
20091029 19:47:30 +0000 INFO [run_cmd] Processing hosts in file ‘admin/etc/servers.mysql.txt’
20091029 19:47:30 +0000 INFO [run_cmd] Running Command ‘hostname’
20091029 19:47:30 +0000 INFO [run_cmd] . . . prdmydb1.example.com
20091029 19:47:30 +0000 INFO [run_cmd] . . . prdmydb2.example.com
20091029 19:47:30 +0000 INFO [run_cmd] . . . betamysql01.example.com
20091029 19:47:30 +0000 INFO [run_cmd] . . . bfprdmydb1a.example.com
20091029 19:47:31 +0000 INFO [run_cmd] . . . bfprdmydb1b.example.com
20091029 19:47:31 +0000 INFO [run_cmd] . . . bfprdmydb2a.example.com
20091029 19:47:31 +0000 INFO [run_cmd] . . . bfhbetamydb1a.beta.example.com
20091029 19:47:31 +0000 INFO [run_cmd] . . . bfhbetamydb1b.beta.example.com
…
$ touch /tmp/run_cmd.stop
…
20091029 19:47:34 +0000 INFO [run_cmd] . . . cemwcmsmysql01.eao.abn-sjc.ea.com
20091029 19:47:37 +0000 WARN [run_cmd] A stop file was provided to stop script processing.
20091029 19:47:37 +0000 INFO [run_cmd] Script completed successfully (8 secs)
$ admin/scripts/run_cmd.sh -h admin/etc/servers.mysql.txt -c hostname
20091029 19:47:48 +0000 ERROR [run_cmd] An existing stop file ‘/tmp/run_cmd.stop’ exists, remove this to start processing
20091029 19:47:48 +0000 INFO [run_cmd] Exiting with status code of ‘1’
4.2 MySQL Standards
4.2.1 Environment Variables
- MYSQL_HOME is never to be specified in a script by default. This should always be defined and accepted by the calling environment.
- The MySQL binaries should always be included within the default path and not specifically included in any script.
/etc/profile/mysql.sh
This file should define the following variables.
MYSQL_HOME=/opt/mysql
PATH=${MYSQL_HOME}/bin:$PATH
4.2.2 Authenticated Access
- A ~root/.my.cnf with a password for the MySQL ‘root’ super user should never be defined. This gives anybody with Operating System root access, and without any knowledge or understanding of MySQL full power to do anything in MySQL. The MySQL ‘root’ user, especially the users with WITH GRANT OPTION privileges should never be used for automated processing.
- For processing of information when a specific task can be performed without a user requiring SUPER privilege it is sufficient to defined the username and password within a script. This user must also have only localhost permissions. For example, to view the processes with SHOW FULL PROCESSLIST, a localhost only user can be defined with the PROCESS privilege.
- For scripts requiring SUPER privileges greater care is necessary. The mysql user should be restricted to localhost permissions, which requires the script to be run locally. A ~user/.my.cnf file can be used when a separate user providing additional operating system security including the user with no login permissions exists, and scripts can be executed via root with su – user command or via sudo command. While ideal, this is impractical for EA at this time due to delegated security among groups.
- There is no other authentication model appropriate for MySQL access, the need to supply a username and password is the only option. After restricting access of scripts to given requirements, and physical execution access, the current ~mysql/.my.cnf #eadba approach is a sufficient implementation of password obfuscation.
- If a global non localhost SUPER privileged user be required for database administration, it’s password should not be in a file as a general security precaution.
The admin/scripts/mysql.sh script should be used as this is a central location of authentication management for MySQL SUPER access. This script was specifically written to enable a change in the model of authenticated access in a single location. This scripts accepts a single SQL Statement, and can also if specified with -t, provide a single value output for ease of use in calling scripts.
$ ./mysql.sh –help
Usage: mysql.sh [ -t | -q | -v | –help | –version ] ‘SQL COMMAND’
Required:
Optional:
-q Quiet Mode. Errors and Warnings only
-t Tail output to last line. Used for checklist
-v Verbose logging
–help Script help
–version Script version (0.01 22-OCT-2009 $)