1 # Copyright (c) 2022 Nokia
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Symptomdata collection is triggered from the trblmgr ricplt pod. This subsystem provides for xapp interface to subscribe the
16 # symptomdata collection via lwsd pod. When the symptomdata collection is triggered then the xapp gets the callback to collect
19 # If the dynamic registration is needed, then the xapp needs to use the Symptomdata.subscribe(...) method to indicate symptomdata
20 # collection. In case the xapp is set to trblmgr config file then the registration is not needed.
22 # If the xapp has the internal data for symptomdata collection REST call response, it can use the helper methods getFileList and collect
23 # to get the needed files or readymade zipped package for reponse.
30 from requests.exceptions import HTTPError
31 from zipfile import ZipFile
32 from threading import Timer
33 from datetime import datetime
34 from mdclogpy import Logger
36 logging = Logger(name=__name__)
39 class RepeatTimer(Timer):
40 # timer class for housekeeping and file rotating
42 while not self.finished.wait(self.interval):
43 self.function(*self.args, **self.kwargs)
46 class Symptomdata(object):
47 # service is the local POD service id, path the temporal storage, host should be the trblmgr service name
48 def __init__(self, service="", servicehost="", path="/tmp/", lwsduri=None, timeout=30):
57 xapp service host name
59 temporal path where the symptomdata collection is stored
61 lwsd uri for symptomdata dynamic registration
63 timeout for subscription status polling
65 if not os.path.exists(path):
67 self.service = service
68 self.servicehost = servicehost
70 self.lwsduri = lwsduri
71 self.timeout = timeout
73 self.zipfilename = None
74 logging.info("Symptomdata init service:%s path:%s lwsduri:%s timeout:%d" % (self.service, self.path, self.lwsduri, self.timeout))
75 if self.lwsduri is not None:
76 # do the subscription, set to True so that first the query is triggered
78 self.subscribe(args=("",))
79 self.subscribetimer = RepeatTimer(self.timeout, self.subscribe, args=("",))
80 self.subscribetimer.start()
82 # make the symptomdata subscription query to lwsd - dynamic registration (needed if the static config in trblmgr does not have xapp service data)
83 def subscribe(self, args):
86 internally used subscription function if the dynamic registration has been set
88 if self.lwsduri is not None:
90 proxies = {"http": "", "https": ""} # disable proxy usage
91 headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
92 if self.lwsdok is False:
93 jsondata = json.dumps({'url': 'http://' + self.servicehost +
94 ':8080/ric/v1/symptomdata', 'service': self.service, 'instance': '1'})
95 response = requests.post(self.lwsduri,
99 logging.info("Symptomdata subscription success")
101 elif self.lwsdok is True:
103 response = requests.get(self.lwsduri, headers=headers, proxies=proxies)
104 for item in response.json():
105 if item.get('service') == self.service:
106 logging.info("Symptomdata subscription request success")
108 if self.lwsdok is False:
109 logging.error("Symptomdata subscription missing")
110 response.raise_for_status()
111 except HTTPError as http_err:
112 logging.error("Symptomdata subscription failed - http error : %s" % (http_err))
114 except Exception as err:
115 logging.error("Symptomdata subscription failed - error : %s" % (err))
121 stops the dynamic service registration/polling
123 if self.subscribetimer is not None:
124 self.subscribetimer.cancel()
127 if self.subscribetimer is not None:
128 self.subscribetimer.cancel()
130 def getFileList(self, regex, fromtime, totime):
133 internal use only, get the matching files for collect method
136 path, wc = regex.rsplit('/', 1)
137 logging.info("Filtering path: %s using wildcard %s fromtime %d totime %d" % (path + '/', wc, fromtime, totime))
139 for root, dirs, files in os.walk((path + '/')):
140 for filename in files:
141 if re.match(wc, filename):
142 file_path = os.path.join(root, filename)
143 filest = os.stat(file_path)
145 logging.info("Filtering file time %d fromtime %d totime %d" % (filest.st_ctime, fromtime, totime))
146 if fromtime <= filest.st_ctime:
147 logging.info("Adding file time %d fromtime %d" % (filest.st_ctime, fromtime))
149 if totime >= filest.st_ctime:
150 fileList.append(file_path)
152 fileList.append(file_path)
154 if totime >= filest.st_ctime:
155 logging.info("Filtering file time %d fromtime %d totime %d" % (filest.st_ctime, fromtime, totime))
156 fileList.append(file_path)
158 fileList.append(file_path)
161 logging.error("System error %d" % (e.errno))
164 def collect(self, zipfiletmpl, fileregexlist, fromtime, totime):
167 collects the symptomdata based on the file regular expression match and stored the symptomdata. Optionaly
168 caller can use fromtime and totime to choose only files matching the access time
173 template for zip file name using the strftime format - ex: ``"symptomdata"+'-%Y-%m-%d-%H-%M-%S.zip'``
174 fileregexlist: string array
175 array for file matching - ex: ``('examples/*.csv',)``
185 zipfilename = self.path + datetime.fromtimestamp(int(time.time())).strftime(zipfiletmpl)
186 logging.info("Compressing files to symptomdata archive: %s" % (zipfilename))
187 zipdata = ZipFile(zipfilename, "w")
189 self.zipfilename = None
191 for fileregex in fileregexlist:
192 logging.info("Compressing files using %s" % (fileregex))
193 fileList = self.getFileList(fileregex, fromtime, totime)
195 if len(fileList) > 0:
196 for file_path in fileList:
197 logging.info("Adding file %s to archive" % (file_path))
198 zipdata.write(file_path, file_path)
201 logging.error("System error %d" % (e.errno))
204 self.zipfilename = zipfilename
205 return self.zipfilename
210 reads the stored symptomdata file content
219 bytes of the file data
222 with open(self.zipfilename, 'rb') as file:
224 return (self.zipfilename, len(data), data)
227 if self.zipfilename is not None:
228 os.remove(self.zipfilename)