1 | import re
|
---|
2 | import pytz
|
---|
3 | import dateutil
|
---|
4 | import dateutil.parser
|
---|
5 |
|
---|
6 | from ....builtins import *
|
---|
7 |
|
---|
8 | from ...exceptions import *
|
---|
9 | from ...expressions import *
|
---|
10 | from ...autotype import *
|
---|
11 | from ...streams import *
|
---|
12 |
|
---|
13 | from .. import bpplclients
|
---|
14 |
|
---|
15 | re_cannot_connect_on_socket = re.compile('^cannot connect on socket.*$')
|
---|
16 |
|
---|
17 | ##
|
---|
18 | ## bppllist has records separated by blank lines
|
---|
19 | ##
|
---|
20 | def stream(stream, format='bppllist -L'):
|
---|
21 |
|
---|
22 | if format in ['bppllist -L']:
|
---|
23 | return BlankLineStream(stream, header=1)
|
---|
24 | else:
|
---|
25 | raise ParseError, 'Unknown format %s' % (format)
|
---|
26 |
|
---|
27 |
|
---|
28 | ##
|
---|
29 | ## Parse a bppllist record
|
---|
30 | ##
|
---|
31 | ## bppllist -L
|
---|
32 | ##
|
---|
33 | def parse(record, format='bppllist -L', version=None, tz=None):
|
---|
34 |
|
---|
35 | re_pair = re.compile('^\s*([^:]+):\s*(.*)$')
|
---|
36 | re_type_id = re.compile('^(.+)\s+\((\d+)\)$')
|
---|
37 | re_nbu_datetime = re.compile('^.*\d\d:\d\d:\d\d\s+\((\d+)\)$')
|
---|
38 |
|
---|
39 | policy = ExtendedDict()
|
---|
40 |
|
---|
41 | clients = ExtendedDict()
|
---|
42 | includes = []
|
---|
43 | excludes = []
|
---|
44 |
|
---|
45 | policy['clients'] = clients
|
---|
46 | policy['includes'] = includes
|
---|
47 | policy['excludes'] = excludes
|
---|
48 |
|
---|
49 | if re_cannot_connect_on_socket.match(record[0]):
|
---|
50 | return None
|
---|
51 |
|
---|
52 | if format == 'bppllist -L':
|
---|
53 |
|
---|
54 | header = True
|
---|
55 |
|
---|
56 | try:
|
---|
57 |
|
---|
58 | i = 0
|
---|
59 |
|
---|
60 | #
|
---|
61 | # process policy header
|
---|
62 | #
|
---|
63 | while header and i < len(record):
|
---|
64 |
|
---|
65 | line = record[i]
|
---|
66 |
|
---|
67 | match = re_pair.match(line)
|
---|
68 |
|
---|
69 | key = match.group(1)
|
---|
70 | value = match.group(2)
|
---|
71 | key = key.lower()
|
---|
72 | key = key.replace(' ', '_')
|
---|
73 | key = key.replace('.', '')
|
---|
74 | key = key.replace('/', '_')
|
---|
75 |
|
---|
76 | if key == 'schedule':
|
---|
77 | header = False
|
---|
78 | else:
|
---|
79 |
|
---|
80 | #
|
---|
81 | # match timestamp with Unix time in parentheses
|
---|
82 | #
|
---|
83 | match = re_nbu_datetime.match(line)
|
---|
84 | if match:
|
---|
85 | value = datetime.datetime.fromtimestamp(float(match.group(1)), tz)
|
---|
86 |
|
---|
87 | #
|
---|
88 | # clients (entire line can be parsed with bpplclients parser)
|
---|
89 | #
|
---|
90 | elif key == 'client_hw_os_pri':
|
---|
91 | client = bpplclients.parse(line)
|
---|
92 | clients[client.client] = client
|
---|
93 | key = None
|
---|
94 |
|
---|
95 | #
|
---|
96 | # no clients defined
|
---|
97 | #
|
---|
98 | elif key == 'clients' and value == '(none defined)':
|
---|
99 | value = []
|
---|
100 |
|
---|
101 | #
|
---|
102 | # options
|
---|
103 | #
|
---|
104 | elif key == 'options':
|
---|
105 | value = int(value, 16) # convert to integer from hexadecimal string
|
---|
106 |
|
---|
107 | #
|
---|
108 | # includes
|
---|
109 | #
|
---|
110 | elif key == 'include':
|
---|
111 | includes.append(value)
|
---|
112 | key = None
|
---|
113 |
|
---|
114 | #
|
---|
115 | # excludes
|
---|
116 | #
|
---|
117 | elif key == 'exclude':
|
---|
118 | if value != '(none defined)':
|
---|
119 | excludes.append(value)
|
---|
120 | key = None
|
---|
121 |
|
---|
122 | #
|
---|
123 | # effective date does not include a Unix timestamp
|
---|
124 | #
|
---|
125 | elif key == 'effective_date':
|
---|
126 | value = dateutil.parser.parse(value).replace(tzinfo=tz)
|
---|
127 |
|
---|
128 | #
|
---|
129 | # separate type and id
|
---|
130 | #
|
---|
131 | elif re_type_id.match(value):
|
---|
132 | match = re_type_id.match(value)
|
---|
133 | idkey = '%s_id' % (key)
|
---|
134 | value = match.group(1)
|
---|
135 | policy[idkey] = int(match.group(2))
|
---|
136 |
|
---|
137 | #
|
---|
138 | # values to replace with Python None datatype
|
---|
139 | #
|
---|
140 | elif value in ['(none)', '(none specified)', '(none defined)']:
|
---|
141 | value = None
|
---|
142 |
|
---|
143 | #
|
---|
144 | # ..otherwise autodetect
|
---|
145 | #
|
---|
146 | else:
|
---|
147 | value = autotype(value)
|
---|
148 |
|
---|
149 | #
|
---|
150 | #
|
---|
151 | #
|
---|
152 | if key is not None:
|
---|
153 | policy[key] = value
|
---|
154 |
|
---|
155 | i += 1
|
---|
156 |
|
---|
157 | #
|
---|
158 | # parse schedules
|
---|
159 | #
|
---|
160 | schedules = ExtendedDict()
|
---|
161 |
|
---|
162 | while i < len(record):
|
---|
163 |
|
---|
164 | line = record[i]
|
---|
165 |
|
---|
166 | match = re_pair.match(line)
|
---|
167 |
|
---|
168 | #
|
---|
169 | # this is typical key:value metadata
|
---|
170 | #
|
---|
171 | if match:
|
---|
172 |
|
---|
173 | key = match.group(1)
|
---|
174 | value = match.group(2)
|
---|
175 | key = key.lower()
|
---|
176 | key = key.replace(' ', '_')
|
---|
177 | key = key.replace('-', '_')
|
---|
178 | key = key.replace('/', '_')
|
---|
179 |
|
---|
180 | #
|
---|
181 | # identify current schedule
|
---|
182 | #
|
---|
183 | if key == 'schedule':
|
---|
184 |
|
---|
185 | schedule = ExtendedDict()
|
---|
186 |
|
---|
187 | schedule['schedule'] = value
|
---|
188 |
|
---|
189 | schedules[value] = schedule
|
---|
190 |
|
---|
191 | #
|
---|
192 | # type and type_id
|
---|
193 | #
|
---|
194 | elif key == 'type':
|
---|
195 | re_type_id.match(value)
|
---|
196 | match = re_type_id.match(value)
|
---|
197 | idkey = '%s_id' % (key)
|
---|
198 | value = match.group(1)
|
---|
199 | schedule[idkey] = int(match.group(2))
|
---|
200 |
|
---|
201 | #
|
---|
202 | # daily windows
|
---|
203 | #
|
---|
204 | elif key == 'daily_windows':
|
---|
205 |
|
---|
206 | daily_windows = ExtendedDict()
|
---|
207 |
|
---|
208 | re_daily_window_labels = re.compile('\s+')
|
---|
209 | re_daily_window = re.compile('\s+')
|
---|
210 |
|
---|
211 | i += 1
|
---|
212 | line = record[i]
|
---|
213 | labels = []
|
---|
214 | for label in re_daily_window_labels.split(line):
|
---|
215 | if label != '':
|
---|
216 | label = label.lower()
|
---|
217 | label = label.replace('-','_')
|
---|
218 | labels.append(label)
|
---|
219 | label_count = len(labels)
|
---|
220 |
|
---|
221 | i += 1
|
---|
222 | for line in record[i:i+7]:
|
---|
223 |
|
---|
224 | daily_window = ExtendedDict()
|
---|
225 |
|
---|
226 | items = []
|
---|
227 | for item in re_daily_window.split(line):
|
---|
228 | if item != '':
|
---|
229 | item = item.lower()
|
---|
230 | items.append(item)
|
---|
231 |
|
---|
232 | item_count = len(items)
|
---|
233 | for index in range(label_count):
|
---|
234 | label = labels[index]
|
---|
235 | if index < item_count:
|
---|
236 | item = items[index]
|
---|
237 | else:
|
---|
238 | item = None
|
---|
239 | daily_window[label] = item
|
---|
240 |
|
---|
241 | daily_windows[daily_window.day] = daily_window
|
---|
242 |
|
---|
243 | i += 7
|
---|
244 |
|
---|
245 | value = daily_windows
|
---|
246 |
|
---|
247 | #
|
---|
248 | # values to replace with Python None datatype
|
---|
249 | #
|
---|
250 | elif value in ['(none)', '(none specified)', '(none defined)']:
|
---|
251 | value = None
|
---|
252 |
|
---|
253 | #
|
---|
254 | # ..otherwise autodetect
|
---|
255 | #
|
---|
256 | else:
|
---|
257 | value = autotype(value)
|
---|
258 |
|
---|
259 | #
|
---|
260 | #
|
---|
261 | #
|
---|
262 | if key is not None:
|
---|
263 | schedule[key] = value
|
---|
264 |
|
---|
265 | #
|
---|
266 | # this is not typical key:value metadata
|
---|
267 | #
|
---|
268 | else:
|
---|
269 | pass
|
---|
270 |
|
---|
271 | i += 1
|
---|
272 |
|
---|
273 | policy['schedules'] = schedules
|
---|
274 |
|
---|
275 | return policy
|
---|
276 |
|
---|
277 | except Exception, e:
|
---|
278 |
|
---|
279 | for line in record:
|
---|
280 | print line
|
---|
281 |
|
---|
282 | raise ParseError, e
|
---|
283 |
|
---|
284 | else:
|
---|
285 |
|
---|
286 | raise ParseError, 'Unknown format %s' % (format)
|
---|
287 |
|
---|