awips2/deltaScripts/20.3.1/DR8364/updateRCMConfig.py
2022-05-05 12:34:50 -05:00

198 lines
6.7 KiB
Python
Executable file

#!/awips2/python/bin/python3
#
# This script must be run on the host where edex_rcm is installed.
#
import copy
import glob
import logging
import os
import re
import shutil
import sys
from xml.etree.ElementTree import ParseError
from xml.parsers.expat import ErrorString
from xml.parsers.expat import errors
import xml.dom.minidom as minidom
import xml.etree.ElementTree as ET
DR_NUMBER = "8364_8469"
CONFIG_PATHS = ["/awips2/rcm/data/config/persist/config.xml"
]
logging.basicConfig(format='%(asctime)-15s %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.INFO)
log = logging.getLogger("updateRCMConfig.py")
class CommentedTreeBuilder(ET.TreeBuilder):
def __init__(self, *args, **kwargs):
super(CommentedTreeBuilder, self).__init__(*args, **kwargs)
def comment(self, data):
self.start(ET.Comment, {})
self.data(data)
self.end(ET.Comment)
def processDir(directory):
status = 0
log.info("Processing directory: %s", directory)
for root, directories, files in os.walk(directory, topdown=False):
for filePath in files:
if str(filePath).endswith(".xml"):
fullPath = os.path.join(root, filePath)
status |= updateFile(fullPath)
return status
def readHeaderComments(path, rootTag):
with open(path, 'r') as inFile:
orig = inFile.read()
pos = orig.find(rootTag)
start = orig.find("<!--")
while start > 0 and start < pos:
end = orig.find("-->", start + 4)
if end < 0:
# unclosed comment
raise Exception("File contains unclosed comment")
if end > pos:
pos = orig.find(rootTag, end + 3)
start = orig.find("<!--", end + 3)
return orig[0:pos], orig[pos:]
def updateFile(path):
log.info("Processing file: %s", path)
rootTagStr = "config"
rootTag = f"<{rootTagStr}>"
header, xml = readHeaderComments(path, rootTag)
try:
parser = ET.XMLParser(target=CommentedTreeBuilder())
root = ET.XML(xml, parser)
except ParseError as e:
if ErrorString(e.code) == errors.XML_ERROR_JUNK_AFTER_DOC_ELEMENT:
log.info("Skipping xml fragment: %s", path)
elif ErrorString(e.code) == errors.XML_ERROR_NO_ELEMENTS:
log.info("Skipping empty xml document: %s", path)
else:
log.error("Error parsing file: %s \"%s\"", path, ErrorString(e.code))
return 1
except Exception:
log.error("Unable to parse XML path: %s", path)
return 1
if root.tag != rootTagStr:
log.info(f"Not a {rootTagStr} xml file: {path}")
return 0
originalEndpontConfigElement = None
endpointConfigElement = root.find("endpointConfig")
if endpointConfigElement is None:
log.warn("endpointConfig element not found, is this really an RCM config file?")
return 1
originalEndpontConfigElement = copy.deepcopy(endpointConfigElement)
# set up default values for JMS configuration
hostname = "localhost"
port = "5672"
vhost = "edex"
servicePort = "8180"
# if connectionURL element is present,
# parse out existing values for hostname, port, vhost, and remove it
connectionURLElement = endpointConfigElement.find("connectionURL")
if connectionURLElement is not None:
url = connectionURLElement.text
match = re.compile("amqp://[^:]+:[^@]+@/(?P<vhost>[^?]+)\?brokerlist='tcp://(?P<hostname>[^:]+):(?P<port>\d+)'.*").match(url)
if match:
hostname = match.group("hostname")
port = match.group("port")
vhost = match.group("vhost")
endpointConfigElement.remove(connectionURLElement)
else:
log.error(f"Unable to parse url: {url}\nFile not updated.")
# if host element is present from earlier errant delta script,
# change the tag to hostname
hostElement = endpointConfigElement.find("host")
if hostElement is not None:
hostElement.tag = "hostname"
hostnameElement = hostElement
# if hostname element is missing add it
hostnameElement = endpointConfigElement.find("hostname")
if hostnameElement is None:
hostnameElement = ET.SubElement(endpointConfigElement, "hostname")
hostnameElement.text = hostname
# if port is element missing add it
portElement = endpointConfigElement.find("port")
if portElement is None:
portElement = ET.SubElement(endpointConfigElement, "port")
portElement.text = port
# if vhost element is missing add it
vhostElement = endpointConfigElement.find("vhost")
if vhostElement is None:
vhostElement = ET.SubElement(endpointConfigElement, "vhost")
vhostElement.text = vhost
# if servicePort element is missing add it
servicePortElement = endpointConfigElement.find("servicePort")
if servicePortElement is None:
servicePortElement = ET.SubElement(endpointConfigElement, "servicePort")
servicePortElement.text = servicePort
if ET.tostring(endpointConfigElement) != ET.tostring(originalEndpontConfigElement):
log.info("Updating file: %s", path)
backupPath = f"{path}.DR{DR_NUMBER}.bak"
if not os.path.exists(backupPath):
shutil.copy(path, backupPath)
pretty_xml = minidom.parseString(ET.tostring(root, 'utf-8')).toprettyxml(indent=' ' * 4, encoding='UTF-8').decode()
pretty_xml = '\n'.join([line for line in pretty_xml.split('\n') if line.strip()])
text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
pretty_xml = text_re.sub('>\g<1></', pretty_xml)
rootPos = pretty_xml.find(rootTag)
try:
with open(path, 'w') as outFile:
outFile.write(header)
outFile.write(pretty_xml[rootPos:])
except Exception:
log.exception("Unable to update %s", path)
return 1
return 0
def main():
log.info(f"Running delta script for RODO DR {DR_NUMBER}...")
status = 0
searchPaths = CONFIG_PATHS
if len(sys.argv) > 1:
searchPaths = sys.argv[1:]
log.debug("searchPaths: %s", searchPaths)
for sp in searchPaths:
paths = glob.glob(sp)
log.debug("paths: %s", paths)
for path in paths:
if os.path.isdir(path):
status |= processDir(path)
elif os.path.isfile(path):
status |= updateFile(path)
if status:
log.error(f"delta script for RODO {DR_NUMBER} complete with errors")
else:
log.info(f"delta script for RODO {DR_NUMBER} complete")
return status
if __name__ == '__main__':
sys.exit(main())