blob: 8d979bcd2dd49f27dcd21d929218cec77dffecb4 [file] [log] [blame]
ehietala12486342022-06-15 12:21:41 +03001# Copyright (c) 2022 Nokia
2#
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
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
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.
14#
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
17# the symptomdata.
18#
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.
21#
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.
24#
25import os
26import re
27import time
28import requests
29import json
30from requests.exceptions import HTTPError
31from zipfile import ZipFile
32from threading import Timer
33from datetime import datetime
34from mdclogpy import Logger
35
36logging = Logger(name=__name__)
37
38
39class RepeatTimer(Timer):
40 # timer class for housekeeping and file rotating
41 def run(self):
42 while not self.finished.wait(self.interval):
43 self.function(*self.args, **self.kwargs)
44
45
46class 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):
49 """
50 init
51
52 Parameters
53 ----------
54 service: string
55 xapp service name
56 servicehost: string
57 xapp service host name
58 path:
59 temporal path where the symptomdata collection is stored
60 lwsduri:
61 lwsd uri for symptomdata dynamic registration
62 timeout:
63 timeout for subscription status polling
64 """
65 if not os.path.exists(path):
66 os.mkdir(path)
67 self.service = service
68 self.servicehost = servicehost
69 self.path = path
70 self.lwsduri = lwsduri
71 self.timeout = timeout
72 # runtime attrs
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
77 self.lwsdok = True
78 self.subscribe(args=("",))
79 self.subscribetimer = RepeatTimer(self.timeout, self.subscribe, args=("",))
80 self.subscribetimer.start()
81
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):
84 """
85 subscribe
86 internally used subscription function if the dynamic registration has been set
87 """
88 if self.lwsduri is not None:
89 try:
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,
96 data=jsondata,
97 headers=headers,
98 proxies=proxies)
99 logging.info("Symptomdata subscription success")
100 self.lwsdok = True
101 elif self.lwsdok is True:
102 self.lwsdok = False
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")
107 self.lwsdok = True
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))
113 self.lwsdok = False
114 except Exception as err:
115 logging.error("Symptomdata subscription failed - error : %s" % (err))
116 self.lwsdok = False
117
118 def stop(self):
119 """
120 stop
121 stops the dynamic service registration/polling
122 """
123 if self.subscribetimer is not None:
124 self.subscribetimer.cancel()
125
126 def __del__(self):
127 if self.subscribetimer is not None:
128 self.subscribetimer.cancel()
129
130 def getFileList(self, regex, fromtime, totime):
131 """
132 getFileList
133 internal use only, get the matching files for collect method
134 """
135 fileList = []
136 path, wc = regex.rsplit('/', 1)
137 logging.info("Filtering path: %s using wildcard %s fromtime %d totime %d" % (path + '/', wc, fromtime, totime))
138 try:
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)
144 if fromtime > 0:
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))
148 if totime > 0:
149 if totime >= filest.st_ctime:
150 fileList.append(file_path)
151 else:
152 fileList.append(file_path)
153 elif totime > 0:
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)
157 else:
158 fileList.append(file_path)
159
160 except OSError as e:
161 logging.error("System error %d" % (e.errno))
162 return fileList
163
164 def collect(self, zipfiletmpl, fileregexlist, fromtime, totime):
165 """
166 collect
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
169
170 Parameters
171 ----------
172 zipfiletmpl: string
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',)``
176 fromtime: integer
177 time value seconds
178 totime: integer
179 time value seconds
180 Returns
181 -------
182 string
183 zipfile name
184 """
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")
188 self.remove()
189 self.zipfilename = None
190 fileCnt = 0
191 for fileregex in fileregexlist:
192 logging.info("Compressing files using %s" % (fileregex))
193 fileList = self.getFileList(fileregex, fromtime, totime)
194 try:
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)
199 fileCnt += 1
200 except OSError as e:
201 logging.error("System error %d" % (e.errno))
202 zipdata.close()
203 if fileCnt > 0:
204 self.zipfilename = zipfilename
205 return self.zipfilename
206
207 def read(self):
208 """
209 read
210 reads the stored symptomdata file content
211
212 Returns
213 -------
214 string
215 zipfile name
216 integer
217 data lenght
218 bytes
219 bytes of the file data
220 """
221 data = None
222 with open(self.zipfilename, 'rb') as file:
223 data = file.read()
224 return (self.zipfilename, len(data), data)
225
226 def remove(self):
227 if self.zipfilename is not None:
228 os.remove(self.zipfilename)