#!/usr/bin/python3 # u p d a t e c h a n n e l . p y # $Revision: 175 $ # $Author: eckertb $ # $Id: updatechannel.py 175 2014-10-19 19:01:52Z eckertb $ # # Description: # RMS Gateway - update the channel info currently stored in # the winlink system # # RMS Gateway # # Copyright (c) 2004-2014 Hans-J. Barthen - DL5DI # Copyright (c) 2008-2014 Brian R. Eckert - W3SG # # Questions or problems regarding this program can be emailed # to linux-rmsgw@w3sg.org # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # import sys import re import requests import json import platform from xml.etree import ElementTree from optparse import OptionParser from distutils.version import LooseVersion import syslog ################################# # BEGIN CONFIGURATION SECTION ################################# service_config_xml = '/etc/rmsgw/winlinkservice.xml' gateway_config = '/etc/rmsgw/gateway.conf' channel_config_xml = '/etc/rmsgw/channels.xml' py_version_require='2.7.9' ################################# # END CONFIGURATION SECTION ################################# cmdlineparser = OptionParser() cmdlineparser.add_option("-d", "--debug", action="store_true", dest="DEBUG", default=False, help="turn on debug output") (options, args) = cmdlineparser.parse_args() errors = 0 # # dictionaries for config info # ws_config = {} svc_calls = {} gw_config = {} param_roots = {} # # load gateway config # with open(gateway_config) as gwfile: for line in gwfile: if not line.strip().startswith('#'): name, val = line.partition("=")[::2] gw_config[name.strip()] = val.strip() gwfile.close() # # setup syslog # fac = eval('syslog.LOG_' + gw_config['LOGFACILITY'].upper()) mask = eval('syslog.LOG_' + gw_config['LOGMASK'].upper()) syslog.openlog(logoption=syslog.LOG_PID, facility=fac) syslog.setlogmask(syslog.LOG_UPTO(mask)) # # check python version # python_version=platform.python_version() if LooseVersion(python_version) >= LooseVersion(py_version_require): if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'Python Version Check: ' + str(python_version) + ' OK') else: syslog.syslog(syslog.LOG_ERR, 'Need more current Python version, require version: ' + str(py_version_require) + ' or newer') print('Exiting ...') syslog.closelog() sys.exit(1) # # load service config from XML # winlink_service = ElementTree.parse(service_config_xml) winlink_config = winlink_service.getroot() for svc_config in winlink_config.iter('config'): ws_config['WebServiceAccessCode'] = svc_config.find('WebServiceAccessCode').text ws_config['svchost'] = svc_config.find('svchost').text ws_config['svcport'] = svc_config.find('svcport').text ws_config['namespace'] = svc_config.find('namespace').text for svc_ops in svc_config.findall('svcops'): for svc_call in svc_ops: svc_calls[svc_call.tag] = svc_call.text param_roots[svc_call.tag] = svc_call.attrib['paramRoot'] if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'ws_config = {}'.format(ws_config)) if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'svc_calls = {}'.format(svc_calls)) if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'param_roots = {}'.format(param_roots)) # # get gateway callsign from config # the basecall # if 'GWCALL' in gw_config: options.callsign = gw_config['GWCALL'].upper() basecall=options.callsign.split("-", 1)[0] if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'basecallsign = {}'.format(basecall)) # # prepare and make webservice call # headers = {'Content-Type' : 'application/xml'} #headers = { # 'Accept': 'application/xml', # 'Content-Type': 'application/xml' #} # svc_url = 'http://' + ws_config['svchost'] + ':' + ws_config['svcport'] + svc_calls['channeladd'] # Needs to look like this: svc_url = 'https://' + ws_config['svchost'] + svc_calls['channeladd'] + '?' + 'Callsign=' + format(options.callsign) + '&Key=' + format(ws_config['WebServiceAccessCode'] + '&format=json') if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'svc_url = {}'.format(svc_url)) # # load channel info from XML # document = ElementTree.parse(channel_config_xml) rmschannels = document.getroot() if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, "rmschannels xml = {}".format(ElementTree.tostring(rmschannels))) # # Prepare xml parameters for call # channel_add = ElementTree.Element(param_roots['channeladd']) channel_add.set('xmlns:i', 'http://www.w3.org/2001/XMLSchema-instance') channel_add.set('xmlns', ws_config['namespace']) ElementTree.SubElement(channel_add, "BaseCallsign") ElementTree.SubElement(channel_add, "Baud") ElementTree.SubElement(channel_add, "Callsign") ElementTree.SubElement(channel_add, "Frequency") ElementTree.SubElement(channel_add, "GridSquare") ElementTree.SubElement(channel_add, "Mode") ElementTree.SubElement(channel_add, "Power") ElementTree.SubElement(channel_add, "Height") ElementTree.SubElement(channel_add, "Gain") ElementTree.SubElement(channel_add, "Direction") ElementTree.SubElement(channel_add, "Hours") ElementTree.SubElement(channel_add, "ServiceCode") ElementTree.SubElement(channel_add, "Key") #ElementTree.SubElement(channel_add, "Comments") if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'bare channel_add XML = {}'.format(ElementTree.tostring(channel_add))) # # with each configured channel, # populate call xml tree with values from # gateway channel config and make service call # ns = ws_config['namespace'] if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'ns from ws_config = {}'.format(ns)) ns = '{http://www.namespace.org}' if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'ns = {}'.format(ns)) #if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'ns = {}'.format(ns)) for channel in rmschannels.findall("%schannel" % (ns)): if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'channel xml = {}'.format(ElementTree.tostring(channel))) callsign = channel.find("%scallsign" % (ns)).text syslog.syslog(syslog.LOG_INFO, 'Posting channel record updates for {}...'.format(callsign)) # # prepare xml parameters for call # channel_add.find('BaseCallsign').text = channel.find('%sbasecall' % (ns)).text channel_add.find('Baud').text = channel.find('%sbaud' % (ns)).text channel_add.find('Callsign').text = channel.find('%scallsign' % (ns)).text channel_add.find('Direction').text = channel.find('%sdirection' % (ns)).text channel_add.find('Frequency').text = channel.find('%sfrequency' % (ns)).text channel_add.find('Gain').text = channel.find('%sgain' % (ns)).text channel_add.find('GridSquare').text = channel.find('%sgridsquare' % (ns)).text channel_add.find('Height').text = channel.find('%sheight' % (ns)).text channel_add.find('Mode').text = channel.find('%smode' % (ns)).text channel_add.find('Power').text = channel.find('%spower' % (ns)).text channel_add.find('Hours').text = channel.find('%shours' % (ns)).text channel_add.find('ServiceCode').text = channel.find('%sservicecode' % (ns)).text channel_add.find('Key').text = ws_config['WebServiceAccessCode'] # channel_add.find('Comments').text = channel.find('%scomments' % (ns)).text if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'channel_add XML = {}'.format(ElementTree.tostring(channel_add))) # Post the request try: response = requests.post(svc_url, data=ElementTree.tostring(channel_add), headers=headers) except requests.ConnectionError as e: syslog.syslog(syslog.LOG_DEBUG,"Error: Internet connection failure:") syslog.syslog(syslog.LOG_DEBUG, 'svc_url = {}'.format(svc_url)) syslog.syslog(syslog.LOG_DEBUG, str(e)) syslog.closelog() sys.exit(1) if options.DEBUG: syslog.syslog(syslog.LOG_DEBUG, 'Response = {}'.format(response.content)) json_data = response.json() if options.DEBUG: print((json.dumps(json_data, indent=2))) json_dict = json.loads(response.text) # print the return code of this request, should be 200 which is "OK" if options.DEBUG: print(("Request status code: " + str(response.status_code))) if options.DEBUG: print(('Debug: Response =', response.content)) if options.DEBUG: print(("Debug: Content type: " + response.headers['content-type'])) # print('ResponseStatus : ', response.json().get('ResponseStatus')) # # Verify request status code # if response.ok: if options.DEBUG: print("Debug: Good Request status code") else: print(("Debug: Bad Response status code: " + str(response.status_code))) print(('*** Get for', options.callsign, 'failed, ErrorCode =', str(response.status_code))) print(('*** Error code: ' + json_dict['ResponseStatus']['ErrorCode'])) print(('*** Error message: ' + json_dict['ResponseStatus']['Message'])) syslog.closelog() sys.exit(1) # # check for errors coming back first # if json_dict['ResponseStatus']: print(('ResponseStatus not NULL: ', json_dict['ResponseStatus'])) syslog.closelog() sys.exit(1) else: if options.DEBUG: print(('ResponseStatus is NULL: ', json_dict['ResponseStatus'])) syslog.closelog() if errors > 0: sys.exit(1) #else sys.exit(0)