Files
mq-container/internal/mqini/mqini.go
Stephen Marshall 52b4eeea32 Fix MIQM
2019-08-30 11:44:45 +01:00

332 lines
9.1 KiB
Go

/*
© Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package mqini provides information about queue managers
package mqini
import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
// QueueManager describe high-level configuration information for a queue manager
type QueueManager struct {
Name string
Prefix string
Directory string
DataPath string
InstallationName string
}
var qmgrDir string
var stanzasQMINI []string
var stanzasMQATINI []string
// getQueueManagerFromStanza parses a queue manager stanza
func getQueueManagerFromStanza(stanza string) (*QueueManager, error) {
scanner := bufio.NewScanner(strings.NewReader(stanza))
qm := QueueManager{}
for scanner.Scan() {
l := scanner.Text()
l = strings.TrimSpace(l)
t := strings.Split(l, "=")
switch t[0] {
case "Name":
qm.Name = t[1]
case "Prefix":
qm.Prefix = t[1]
case "Directory":
qm.Directory = t[1]
case "DataPath":
qm.DataPath = t[1]
case "InstallationName":
qm.InstallationName = t[1]
}
}
return &qm, scanner.Err()
}
// GetQueueManager returns queue manager configuration information
func GetQueueManager(name string) (*QueueManager, error) {
// dspmqinf essentially returns a subset of mqs.ini, but it's simpler to parse
out, _, err := command.Run("dspmqinf", "-o", "stanza", name)
if err != nil {
return nil, err
}
return getQueueManagerFromStanza(out)
}
// GetErrorLogDirectory returns the directory holding the error logs for the
// specified queue manager
func GetErrorLogDirectory(qm *QueueManager) string {
if qm.DataPath != "" {
return filepath.Join(qm.DataPath, "errors")
}
return filepath.Join(qm.Prefix, "qmgrs", qm.Directory, "errors")
}
//AddStanzas Reads supplied mq ini configuration files and updates the stanzas
//into queue manager's ini configuration files.
func AddStanzas(qmname string) error {
//find the qmgr directory.
qm, err := GetQueueManager(qmname)
if err != nil {
return err
}
qmgrDir = filepath.Join(qm.Prefix, "qmgrs", qm.Directory)
if qm.DataPath != "" {
qmgrDir = qm.DataPath
}
//Find the users ini configuration file
files := getIniFileList()
if len(files) > 1 {
msg := fmt.Sprintf("[ %v ]", files)
return errors.New("Only a single ini file can be provided. Following ini files are found:" + msg)
}
if len(files) == 0 {
//no ini file update required.
return nil
}
iniFileBytes, err := ioutil.ReadFile(files[0])
if err != nil {
return err
}
userconfig := string(iniFileBytes)
if len(userconfig) == 0 {
return nil
}
//Prepare a list of all supported stanzas
PopulateAllAvailableStanzas()
//Update the qmgr ini file with user config.
qmConfig, atConfig, err := PrepareConfigStanzasToWrite(userconfig)
if err != nil {
return err
}
err = writeConfigStanzas(qmConfig, atConfig)
if err != nil {
return err
}
return nil
}
// PopulateAllAvailableStanzas initializes the ini stanzas prescribed by mq specification.
func PopulateAllAvailableStanzas() {
stanzasQMINI = []string{"ExitPath",
"Log",
"Service",
"ServiceComponent",
"Channels",
"TCP",
"ApiExitLocal",
"AccessMode",
"RestrictedMode",
"XAResourceManager",
"DefaultBindType",
"SSL",
"DiagnosticMessages",
"Filesystem",
"Security",
"TuningParameters",
"ExitPropertiesLocal",
"LU62",
"NETBIOS"}
stanzasMQATINI = []string{"AllActivityTrace", "ApplicationTrace"}
}
// getIniFileList Checks for the user supplied ini file in /etc/mqm directory.
func getIniFileList() []string {
fileList := []string{}
filepath.Walk("/etc/mqm", func(path string, f os.FileInfo, err error) error {
if strings.HasSuffix(path, ".ini") {
fileList = append(fileList, path)
}
return nil
})
return fileList
}
//PrepareConfigStanzasToWrite Reads through the user supplied ini config file and prepares list of
//updates to be written into corresponding mq ini files (qm.ini and/or mqat.ini files.)
func PrepareConfigStanzasToWrite(userconfig string) (string, string, error) {
var qminiConfigStr string
var mqatiniConfigStr string
//read the initial version.
iniFileBytes, err := ioutil.ReadFile(filepath.Join(qmgrDir, "qm.ini"))
if err != nil {
return "", "", err
}
qminiConfigStr = string(iniFileBytes)
iniFileBytes, err = ioutil.ReadFile(filepath.Join(qmgrDir, "mqat.ini"))
if err != nil {
return "", "", err
}
mqatiniConfigStr = string(iniFileBytes)
stanzaListMerge := make(map[string]strings.Builder)
stanzaListAppend := make(map[string]strings.Builder)
var sbAppend strings.Builder
var sbMerger strings.Builder
scanner := bufio.NewScanner(strings.NewReader(userconfig))
scanner.Split(bufio.ScanLines)
consumetoAppend := false
consumeToMerge := false
var stanza string
//read through the user file and prepare what we want.
for scanner.Scan() {
if strings.Contains(scanner.Text(), ":") {
consumetoAppend = false
consumeToMerge = false
stanza = scanner.Text()
//check if this stanza exists in the qm.ini/mqat.ini files
if strings.Contains(qminiConfigStr, stanza) ||
(strings.Contains(mqatiniConfigStr, stanza) && !(strings.Contains(stanza, "ApplicationTrace"))) {
consumeToMerge = true
sbMerger = strings.Builder{}
stanzaListMerge[stanza] = sbMerger
} else {
consumetoAppend = true
sbAppend = strings.Builder{}
stanzaListAppend[stanza] = sbAppend
}
} else {
if consumetoAppend {
sb := stanzaListAppend[stanza]
sb.WriteString(scanner.Text() + "\n")
stanzaListAppend[stanza] = sb
}
if consumeToMerge {
sb := stanzaListMerge[stanza]
sb.WriteString(scanner.Text() + "\n")
stanzaListMerge[stanza] = sb
}
}
}
//merge if stanza exits.
if len(stanzaListMerge) > 0 {
for key := range stanzaListMerge {
toWrite, filename := ValidateStanzaToWrite(key)
if toWrite {
attrList := stanzaListMerge[key]
switch filename {
case "qm.ini":
qminiConfigStr = prepareStanzasToMerge(key, attrList, qminiConfigStr)
case "mqat.ini":
mqatiniConfigStr = prepareStanzasToMerge(key, attrList, mqatiniConfigStr)
default:
}
}
}
}
//append new stanzas.
if len(stanzaListAppend) > 0 {
for key := range stanzaListAppend {
attrList := stanzaListAppend[key]
if strings.Contains(strings.Join(stanzasMQATINI, ", "), strings.TrimSuffix(strings.TrimSpace(key), ":")) {
mqatiniConfigStr = prepareStanzasToAppend(key, attrList, mqatiniConfigStr)
} else {
qminiConfigStr = prepareStanzasToAppend(key, attrList, qminiConfigStr)
}
}
}
return qminiConfigStr, mqatiniConfigStr, nil
}
//ValidateStanzaToWrite Validates stanza to be written and the file it belongs to.
func ValidateStanzaToWrite(stanza string) (bool, string) {
stanza = strings.TrimSpace(stanza)
if strings.Contains(stanza, ":") {
stanza = stanza[:len(stanza)-1]
}
if strings.Contains(strings.Join(stanzasQMINI, ", "), stanza) {
return true, "qm.ini"
} else if strings.Contains(strings.Join(stanzasMQATINI, ", "), stanza) {
return true, "mqat.ini"
} else {
return false, ""
}
}
//prepareStanzasToAppend Prepares list of stanzas that are to be appended into qm ini files(qm.ini/mqat.ini)
func prepareStanzasToAppend(key string, attrList strings.Builder, iniConfig string) string {
newVal := key + "\n" + attrList.String()
iniConfig = iniConfig + newVal
return iniConfig
}
//prepareStanzasToMerge Prepares list of stanzas that are to be updated into qm ini files(qm.ini/mqat.ini)
//These stanzas are already present in mq ini files and their values have to be updated with user supplied ini.
func prepareStanzasToMerge(key string, attrList strings.Builder, iniConfig string) string {
lineScanner := bufio.NewScanner(strings.NewReader(attrList.String()))
lineScanner.Split(bufio.ScanLines)
for lineScanner.Scan() {
attrLine := lineScanner.Text()
keyvalue := strings.Split(attrLine, "=")
//this line present in qm.ini, update value.
if strings.Contains(iniConfig, keyvalue[0]) {
re := regexp.MustCompile(keyvalue[0] + "=.*")
iniConfig = re.ReplaceAllString(iniConfig, attrLine)
} else { //this line not present in qm.ini file, add it.
re := regexp.MustCompile(key)
newVal := key + "\n" + attrLine
iniConfig = re.ReplaceAllString(iniConfig, newVal)
}
}
return iniConfig
}
//writeConfigStanzas Writes the ini file updates into corresponding mq ini files.
func writeConfigStanzas(qmConfig string, atConfig string) error {
err := ioutil.WriteFile(filepath.Join(qmgrDir, "qm.ini"), []byte(qmConfig), 0644)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(qmgrDir, "mqat.ini"), []byte(atConfig), 0644)
if err != nil {
return err
}
return nil
}