9f3eff408d73487f21fbc22f228327b80c780b53
[iooclient] / iooclient / eos_pmd.py
1 # Copyright 2012 Leonidas Poulopoulos
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 from eos import eos
16 import sys
17 from pyparsing import *
18
19 class eosPmd(eos):
20     '''
21     Subclass of eos to connect to the PM subsystem
22     '''
23     
24     def __init__(self, server=None, port=None, password=None):
25         '''
26         
27         Constructor for the class.
28
29         @type server: String
30         @param server: The Hostname or IPv4 address of the Alcatel NMS.
31         
32         @type port: Integer
33         @param port: The TCP port on the Alcatel NMS we will connect to.
34         
35         @type password: String
36         @param password: The password for this connection.
37         
38         @rtype: Handler
39         @return: A connection handler of this class.
40         '''
41         if port == None:
42             port = 20007
43         return eos.__init__(self, server, port, password)
44     
45     def list_pm_data(self, startPeriod=None, endPeriod=None, neName=[], periodType=None, counters=[], pmPoints=[], parse=True):
46         '''
47         Function used to obtain a listing of of PM DATA from NMS. Depending on parse value the output is either raw data from NMS 
48         or structured python data
49         
50         Usage:
51         
52             >>> from iooclient.eos_pmd import * 
53             >>> pmd = eosPmd('1.2.3.4', 20007, 'password')
54             >>> pmd.list_pm_data()
55             [{'{'friendlyName': 'elementName',
56               'neLocationName': Athens,
57               'notificationType': 'pmPointList',
58               ...
59         
60         or:
61         
62             >>> from iooclient.eos_pmd import * 
63             >>> with eosPmd('1.2.3.4', 20007, 'password') as pmd:
64             >>>    pmd.list_pm_data()
65             [{'{'friendlyName': 'elementName',
66               'neLocationName': Athens,
67               'notificationType': 'pmPointList',
68               ... 
69         
70         @type startPeriod: String
71         @param startPeriod: From when to collect PM data. Format: YYYYMMDDQH where QH in [00,96] representing 15min periods in a day.
72         
73         @type endPeriod: String
74         @param endPeriod: To when to collect PM data. Format: YYYYMMDDQH where QH in [00,96] representing 15min periods in a day.
75         
76         @type neName: A list of strings
77         @param neName: A list of NetworkElement names with pmPoints assigned. If omitted all NEs are assumed
78                 
79         @type periodType: String
80         @param periodType: The period of monitoring. Values: 15, 1h, 24, RM1, RM2, RM3, RM4, RM5, FL, immediate, peak, 30-day, IM, day, month. Default 15
81         
82         @type counters: A list of strings
83         @param counters: The counters for the above NetworkElements to fetch. If omitted all counters
84         
85         @type pmPoints: A list of strings
86         @param pmPoints: The counters for the above NetworkElements to fetch. If omitted all counters
87         
88         @type parse: Boolean
89         @param parse: Default: True. If set to True, the output is a python list of dictionaries. Otherwise the output is the NMS response text.   
90         
91         @rtype: String or list of dictionaries
92         @return: Performance monitoring data (counter values) in raw text format or in structured format. False in case of errors.
93         '''
94         extras = []
95         extras.append('periodType=%s'%(periodType) if periodType else '')
96         extras.append('startPeriod=%s'%(startPeriod) if startPeriod else '')
97         extras.append('endPeriod=%s'%(endPeriod) if endPeriod else '')
98         extras.append('counters={%s}'%(('|'.join(counters)) if counters else ''))
99         pmpoints = '{%s}' %('|'.join(["pmPoint="+pmPoint for pmPoint in pmPoints])) if pmPoints else None
100         extras.append('neName=%s'%(''.join([name+(pmpoints or '{}') for name in neName])) if neName else '')
101         params = "|".join(extras) or ''
102         message = "LIST_PM_DATA_REQ[%s]" %params
103         if self.send(message) == False:
104             return False
105         reply_header = self.get_part()
106         if reply_header != "LIST_PM_DATA_CONF[]" :
107             print "Error: Asked for %s but got unexpected output: %s\n" %(message, reply_header)
108             return False
109         reply = self.get()
110         if reply == "DATA_END_NOTIF[]":
111             return False
112         if parse:
113             ret = self.parsePmData(reply)
114         else:
115             ret = reply
116         return ret
117     
118     
119     def list_pm_point(self, neNames=[], periodType=None, period=None, parse=True, listAll=False):
120         '''
121         Function used to obtain a listing of of PM points from NMS. Depending on parse value the output is either raw data from NMS 
122         or structured python data 
123         
124         Usage:
125             
126             >>> from iooclient.eos_pmd import * 
127             >>> pmd = eosPmd('1.2.3.4', 20007, 'password')
128             >>> pmd.list_pm_point()
129             [{'{'friendlyName': 'elementName',
130               'neLocationName': Athens,
131               'notificationType': 'pmPointList',
132               ...
133             
134         or:
135             
136             >>> from iooclient.eos_pmd import * 
137             >>> with eosPmd('1.2.3.4', 20007, 'password') as pmd:
138             >>>    pmd.list_pm_point()
139             [{'{'friendlyName': 'elementName',
140               'neLocationName': Athens,
141               'notificationType': 'pmPointList',
142             ...
143         
144         @type neNames: A list of strings
145         @param neNames: A list of NetworkElement names with pmPoints assigned. If omitted, all NEs are assumed
146                 
147         @type periodType: String
148         @param periodType: The period of monitoring. Values: 15, 1h, 24, RM1, RM2, RM3, RM4, RM5, FL, immediate, peak, 30-day, IM, day, month. Default 15
149                
150         @type period: String
151         @param period: Period during which pm points were collected. Format: YYYYMMDDQH where QH in [00,96] representing 15min periods in a day.
152         
153         @type parse: Boolean
154         @param parse: Default: True. If set to True, the output is a python list of dictionaries. Otherwise the output is the NMS response text.
155         
156         @type listAll: Boolean
157         @param listAll: Default: False. If set to True, the output will be all points regardless of the default period set. Else, only the default period data will be fetched
158         
159         @rtype: String or list of dictionaries
160         @return: Performance monitoring points (counter names) in raw text or in structured format. False in case of errors.
161         '''
162         extras = []
163         extras.append('%s'%(periodType) if periodType else '')
164         extras.append('%s'%(period) if period else '')
165         extras.append('{%s}'%(('|'.join(neNames)) if neNames else ''))
166         params = "|".join(extras) or ''
167         if listAll:
168             params=''
169         message = "LIST_PM_POINT_REQ[%s]" %params
170         if self.send(message) == False:
171             return False
172         reply_header = self.get_part()
173         if reply_header != "LIST_PM_POINT_CONF[]" :
174             print "Error: Asked for %s but got unexpected output: %s\n" %(message, reply_header)
175             return False
176         reply = self.get()
177         if reply == "DATA_END_NOTIF[]":
178             return False
179         if parse:
180             ret = self.parsePmPoint(reply)
181         else:
182             ret = reply
183         return ret
184         
185     def setGroupCounters(self, l, s, toks):
186         ''' 
187         Pyparsing action parser to include counters as key in pmPoint parsing
188         '''
189         return [["counters", toks[0][0].asList()]]
190     
191     def setGroupPmpoints(self, l, s, toks):
192         ''' 
193         Pyparsing action parser to include pmpoints as key in pmPoint parsing
194         '''
195         return [["pmpoints", toks[0][0].asList()]]
196     
197     def setGroupPeriods(self, l, s, toks):
198         ''' 
199         Pyparsing action parser to include periods as key in pmPoint parsing
200         '''
201         return [["periods", toks[0][0].asList()]]
202     
203     def parsePmPoint(self, data):
204         ''' 
205         Pyparsing pmpoint parser
206         
207         @type data: String
208         @param data: Raw output from the NMS.
209         
210         @rtype: list of dicts
211         @return: Transforms the NMS string into python list of dictionaries using the NMS delimiters
212         '''
213         escapechar = "\\"
214         wordtext = CharsNotIn('\\*?^:"[]|={}')
215         escape = Suppress(escapechar) + (Word(printables, exact=1) | White(exact=1))
216         wordvalues = Combine(OneOrMore(wordtext | escape | '=>')).setParseAction(lambda t:t[0].replace('\xb0',''))
217         rStart = Suppress('LIST_CUR_NE_DIR_CONF[]')
218         rEnd = Suppress('DATA_END_NOTIF[]')
219         response_body = Forward()
220         response = response_body + rEnd
221         pmCounters = Group(wordvalues + Suppress('=') + (wordvalues | (Suppress(Optional('{')) + Group(delimitedList(wordvalues, "|"))  + Suppress(Optional('}')))))
222         pmPointBraces = Group(Suppress('{') + Dict(delimitedList(pmCounters, "|")) + Suppress('}'))
223         pmPointsBraces = Group(Group(Suppress('{') + OneOrMore(pmPointBraces) + Suppress('}'))).setParseAction(self.setGroupPmpoints)
224         pmPointInfo = Group(wordvalues + Suppress('=') + (wordvalues | Empty().setParseAction(replaceWith(None))))
225         pmPointContent = Dict(delimitedList(pmPointInfo, "|")) + pmPointsBraces
226         pmPointExpression = Group( Suppress('PM_POINT_NOTIF') + Suppress('[')+pmPointContent+Suppress(']'))
227         allPoints = OneOrMore(pmPointExpression)
228         response_body << allPoints
229         d = response.parseString(data)
230         e = d.asList()
231         return parsePmpoints(e)
232      
233     def parsePmData(self, data):
234         ''' 
235         Pyparsing pmdata parser
236         
237         @type data: String
238         @param data: Raw output from the NMS.
239         
240         @rtype: list of dicts
241         @return: Transforms the NMS string into python list of dictionaries using the NMS delimiters
242         '''
243         escapechar = "\\"
244         wordtext = CharsNotIn('\\*?^:"[]|={}')
245         escape = Suppress(escapechar) + (Word(printables, exact=1) | White(exact=1))
246         wordvalues = Combine(OneOrMore(wordtext | escape | '=>')).setParseAction(lambda t:t[0].replace('\xb0',''))
247         rEnd = Suppress('DATA_END_NOTIF[]')
248         response_body = Forward()
249         response = response_body + rEnd
250         equalityexpr = Group(wordvalues + Suppress('=') + (wordvalues | Empty().setParseAction(replaceWith(None))))
251         dlist = Dict(delimitedList(equalityexpr, "|"))
252         list = delimitedList(equalityexpr, "|")
253         pmCounter = Suppress('{') + Group(Group(dlist)).setParseAction(self.setGroupCounters) + Suppress('}')
254         pmCounterDates = Group(dlist + pmCounter)
255         pmCounters = Group(Group(OneOrMore(pmCounterDates))).setParseAction(self.setGroupPeriods)
256         pmCountersBraces = Suppress('{') + pmCounters + Suppress('}')
257         pmPoint = Group(equalityexpr + pmCountersBraces)
258         pmPoints = Group(Group(OneOrMore(pmPoint))).setParseAction(self.setGroupPmpoints)
259         pmPointsBraces = Suppress('{') + pmPoints + Suppress('}')
260         pmDataNotif = dlist + pmPointsBraces
261         pmDataNotifExpr = Group( Suppress('PM_DATA_NOTIF') + Suppress('[')+pmDataNotif+Suppress(']'))
262         pmDataAll = OneOrMore(pmDataNotifExpr)
263         d = pmDataAll.parseString(data)
264         e = d.asList()
265         return parsePmpoints(e)
266
267 def isComplex(item):
268     '''
269     Simple expression to determine whether a variable is a complex (non-simple or string) list
270     Complex list: [['a','b'], ['d','c']]
271     Simple list: ['a', 'b', 'c']
272     
273     @type item: List or String
274     @param item: The variable to be evaluated
275     
276     @rtype: Boolean
277     @return: True if item is a complex list else False if item is simple list or string
278     '''
279     if isSimpleList(item) or isinstance(item, basestring):
280         return False
281     
282     return True
283
284 def isSimpleList(item):
285     '''
286     Simple expression to determine whether a variable is a simple list
287     Simple list: ['a', 'b', 'c']
288     
289     @type item: List or String
290     @param item: The variable to be evaluated
291     
292     @rtype: Boolean
293     @return: True if item is a simple list else False
294     '''
295     ret = False
296     if isinstance(item, list):
297         try:
298             b = set(item)
299             ret = True
300         except:
301             ret = False
302     return ret
303
304 dictroot = {}
305 '''
306 Global dict variable that assists in transforming the pyparsing output 
307 to a python structured output
308 '''
309
310 def reDict(item):
311     '''
312     Multi-level dictionarize function. Transforms recursively lists and complex lists 
313     into dictionaries
314         
315     @type item: List
316     @param item: The variable to be evaluated
317     
318     @rtype: Dict
319     @return: Dictionary of items
320     '''
321     if isSimpleList(item):
322         return {item[0]: item[1]}
323     dictroot = dict(item)
324     for key,value in dictroot.items():
325         if isinstance(value,list):
326             if isComplex(value):
327                 dictroot[key] = [reDict(i) for i in value]
328     return dictroot
329
330 def parsePmpoints(items):
331     '''
332     Parses lists of lists of lists...etc into list of dictionaries of lists or dicts...etc
333     
334     @type items: List of lists of lists...
335     @param items: Output from the pyparsing function
336     
337     @rtype: List
338     @return: List of dicts of lists of dicts...etc
339     '''
340     l = []
341     for i in items:
342         l.append(reDict(i))
343     return l
344