2 Created on Jan 24, 2014
10 from xml.etree import ElementTree as ET
16 def loadXml(file_name):
17 path_to_xml = os.path.join('', file_name)
18 path_to_xml = os.path.abspath(__file__ + '/../../../' + path_to_xml)
19 with open(path_to_xml) as f:
21 xml_string = re.sub(' xmlns="[^"]+"', '', xml_string, count=1)
23 tree = ET.fromstring(xml_string)
24 return tree, xml_string
27 def buildXmlDocDictionaryForComarableElements(element, flow_dict, p_elm_name=None, kwd=None, akwd=None, mkwd=None):
28 act_key_dict = kwd if (kwd > None) else akwd if (akwd > None) else mkwd if (mkwd > None) else None
30 elm_alias = element.tag if (act_key_dict.get(element.tag, None) > None) else None
31 if ((element.getchildren() > None) & (len(element.getchildren()) > 0)):
32 for child in element.getchildren() :
33 if (element.tag == 'match') :
34 Loader.buildXmlDocDictionaryForComarableElements(child, flow_dict, mkwd=mkwd)
35 elif (element.tag == 'actions') :
36 Loader.buildXmlDocDictionaryForComarableElements(child, flow_dict, akwd=akwd)
38 Loader.buildXmlDocDictionaryForComarableElements(child, flow_dict, elm_alias, kwd, akwd, mkwd);
40 if element.text > None :
41 text = re.sub( '[\s]+','', element.text, count=1)
42 a_key = p_elm_name if (p_elm_name > None) else element.tag
43 flow_dict[a_key] = text;
54 fields to check, arguments:
55 key: element tag from keywords and xml
56 bits: expected length in bits
57 prerequisites: dictionary of elements tag from xml which are required for this field and their values in list
58 or [None] if value is undefined or it's irrelevant (we just need to check if tag is set)
59 convert_from: format in which is value, that is checked against prerequisite values stored in xml
64 prerequisites: {'ethernet-type': [2048]}
67 OF_IPV4_SRC = Field('ipv4-source', 32, {'ethernet-type': [2048]}, 10)
68 IN_PHY_PORT = Field('in-phy-port', 32, {'in-port': [None]}, 10)
71 def __init__(self, key, bits, prerequisites=None, convert_from=10, value_type=type_int):
74 if prerequisites is not None:
75 self.prerequisites = dict(prerequisites)
77 self.prerequisites = None
78 self.convert_from = convert_from
79 self.value_type = value_type
82 return "Field: {0}, size: {1}, prerequisites: {2}"\
83 .format(self.key, self.bits, self.prerequisites)
88 log = logging.getLogger('XMLValidator')
90 channel = logging.StreamHandler()
91 log.addHandler(channel)
93 def __init__(self, kwd, akwd, mkwd, loglevel=logging.INFO):
95 self.test_name = 'No test loaded'
96 XMLValidator.log.setLevel(loglevel)
100 self.flow_dict = dict()
106 def create_dictionaries(self, file_name):
107 self.test_name = file_name
109 formatter = logging.Formatter('TEST {0}: %(levelname)s: %(message)s'.format(self.test_name))
110 XMLValidator.channel.setFormatter(formatter)
112 self.flow_dict = dict()
113 treeXml1, self.xml_string = Loader.loadXml(file_name)
114 Loader.buildXmlDocDictionaryForComarableElements(treeXml1, self.flow_dict, kwd=self.kwd, akwd=self.akwd, mkwd=self.mkwd)
115 XMLValidator.log.debug('loaded dict from xml: {0}'.format(self.flow_dict))
118 def fill_fields(self):
119 Matchers.fill_validator(self)
121 def add_field(self, fields):
122 self.fields.append(fields)
124 def integer_check(self, value, bits, convert_from=10):
125 XMLValidator.log.debug('validating integer: {0}'.format(value))
126 if (int(value, convert_from) / 2**bits) > 0:
127 XMLValidator.log.error('value: {0} is larger than expected: {1}'.format(value, 2**bits))
130 def boolean_check(self, value, bits):
131 XMLValidator.log.debug('validating boolean: {0}'.format(value))
133 XMLValidator.log.error('value: {0} is larger than expected: {1}'.format(value, 2**bits))
136 def ethernet_check(self, a):
137 XMLValidator.log.debug('validating ethernet address: {0}'.format(a))
138 numbers = a.split(':')
139 max_range = (2**8) - 1
142 if int(n, 16) > max_range:
143 XMLValidator.log.error('octet: {0} in ethernet address: {1} larger than: {2}'.format(n, a, max_range))
146 def mask_check(self, address, mask, base=16, part_len=16, delimiter=':'):
147 if (int(mask) % part_len) != 0:
148 raise StandardError('{0} is not valid mask, should be multiples of {1}'.format(mask, part_len))
150 part_count = int(mask) / part_len
152 for part in address.split(delimiter):
153 part_value = int(part, base) if part != '' else 0
156 if part_count < 0 and part_value != 0:
157 raise StandardError('address part {0} should be 0'.format(part))
159 def ipv4_check(self, a):
160 XMLValidator.log.debug('validating ipv4 address: {0}'.format(a))
162 mask_pos = a.find('/')
164 a_mask = a[mask_pos + 1:]
166 self.mask_check(a, a_mask, 10, 8, '.')
168 numbers = a.split('.')
169 max_range = (2**8) - 1
172 if int(n) > max_range:
173 raise StandardError('octet: {0} in ipv4 address: {1} larger than: {2}'.format(n, a, max_range))
175 def ipv6_check(self, a):
176 XMLValidator.log.debug('validating ipv6 address: {0}'.format(a))
177 mask_pos = a.find('/')
179 a_mask = a[mask_pos + 1:]
181 self.mask_check(a, a_mask)
183 numbers = a.split(':')
184 max_range = (2**16) - 1
187 #if n == '' then the number is 0000 which is always smaller than max_range
188 if n != '' and int(n, 16) > max_range:
189 raise StandardError('number: {0} in ipv6 address: {1} larger than: {2}'.format(n, a, max_range))
191 def check_size(self, value, bits, value_type, convert_from=10):
192 XMLValidator.log.debug('checking value: {0}, size should be {1} bits'.format(value, bits))
193 ipv6_regexp = re.compile("^[0-9,A-F,a-f]{0,4}(:[0-9,A-F,a-f]{0,4}){1,7}(/[0-9]{1,3})?$")
194 ipv4_regexp = re.compile("^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$")
195 ethernet_regexp = re.compile("^[0-9,A-F,a-f]{2}(:[0-9,A-F,a-f]{2}){5}$")
198 if value_type == type_boolean and value in ['true', 'false']: #boolean values
199 self.boolean_check(value, bits)
200 elif value_type == type_ethernet and ethernet_regexp.match(value): #ethernet address
201 self.ethernet_check(value)
202 elif value_type == type_ipv4 and ipv4_regexp.match(value): #IPV4 address
203 self.ipv4_check(value)
204 elif value_type == type_ipv6 and ipv6_regexp.match(value): #IPV6 address
205 self.ipv6_check(value)
206 elif value_type == type_int: #integer values
207 self.integer_check(value, bits, convert_from)
211 XMLValidator.log.info('size of: {0} < 2^{1} validated successfully'.format(value, bits))
214 XMLValidator.log.error('problem converting value to int or IP addresses: {0}'.format(value))
218 XMLValidator.log.error('problem converting value: {0}, TypeError'.format(value))
221 except StandardError as e:
222 XMLValidator.log.error('problem checking size for value: {0}, reason: {1}'.format(value, str(e)))
226 def has_prerequisite(self, key, values, convert_from, flow_dict):
227 XMLValidator.log.debug('checking prerequisite: {0} - {1}'.format(key, values))
229 flow_value_raw = flow_dict[key]
231 #if prerequisites values are [None] we don't care about actual value
233 flow_value = int(flow_value_raw, convert_from)
235 if flow_value not in values:
236 raise StandardError()
238 XMLValidator.log.info('prerequisite {0}: {1} to value {2} validated successfully'.format(key, values, flow_value_raw))
241 XMLValidator.log.error('can\'t find element: {0} in xml {1} or in keywords {2}'.format(key, self.xml_string, self.mkwd.keys()))
244 except ValueError or TypeError:
245 # flow_value_raw is string that cannot be converted to decimal or hex number or None
246 if flow_value_raw not in values:
247 XMLValidator.log.error('can\'t find element: {0} with value value: {1} '
248 'in expected values {2}'.format(key, flow_value_raw, values))
251 XMLValidator.log.info('prerequisite {0}: {1} to value {2} validated successfully'.format(key, values, flow_value_raw))
253 except StandardError:
254 XMLValidator.log.error('can\'t find element: {0} with value value: {1} '
255 'in expected values {2}'.format(key, flow_value, values))
258 def check_all_prerequisites(self, prerequisites_dict, convert_from, flow_dict):
259 XMLValidator.log.debug('checking prerequisites: {0}'.format(prerequisites_dict))
260 for k, v in prerequisites_dict.items():
261 self.has_prerequisite(k, v, convert_from, flow_dict)
263 def check_single_field(self, field, flow_dict):
265 @type field MatchField
269 if field.key not in flow_dict:
270 XMLValidator.log.debug('{0} is not set in XML, skipping validation'.format(field.key))
273 XMLValidator.log.info('validating: {0}'.format(field))
275 if field.bits is not None:
276 self.check_size(flow_dict[field.key], field.bits, field.value_type, field.convert_from)
278 if field.prerequisites is not None:
279 self.check_all_prerequisites(field.prerequisites, field.convert_from, flow_dict)
281 def validate_fields(self):
283 XMLValidator.log.info('validating against flow: {0}'.format(self.flow_dict))
284 for field in self.fields:
285 self.check_single_field(field, self.flow_dict)
287 def validate_misc_values(self):
288 for kw in self.kwd.keys():
289 if kw in self.flow_dict.keys():
290 XMLValidator.log.info('validating: {0}: {1}'.format(kw, self.flow_dict[kw]))
292 value = int(self.flow_dict[kw])
294 XMLValidator.log.error('value: {0}: {1} should be non-negative'.format(kw, self.flow_dict[kw]))
297 XMLValidator.log.info('value: {0}: {1} validated successfully'.format(kw, self.flow_dict[kw]))
298 except StandardError:
299 XMLValidator.log.error('can\'t convert value: {0}: {1} to integer'.format(kw, self.flow_dict[kw]))
302 XMLValidator.log.debug('{0} is not set in XML, skipping validation'.format(kw))
305 self.validate_fields()
306 self.validate_misc_values()
308 XMLValidator.log.info('XML valid: {0}'.format(self.xml_ok))
314 IN_PORT = Field('in-port', 32)
315 IN_PHY_PORT = Field('in-phy-port', 32, {'in-port': [None]})
316 METADATA = Field('metadata', 64, convert_from=16)
318 ETH_DST = Field('ethernet-source', 48, value_type=type_ethernet)
319 ETH_SRC = Field('ethernet-destination', 48, value_type=type_ethernet)
320 ETH_TYPE = Field('ethernet-type', 16)
322 VLAN_VID = Field('vlan-id', 13)
323 VLAN_PCP = Field('vlan-pcp', 3, {'vlan-id': [None]})
325 IP_DSCP = Field('ip-dscp', 6, {'ethernet-type': [2048, 34525]})
326 IP_ENC = Field('ip-ecn', 2, {'ethernet-type': [2048, 34525]})
327 IP_PROTO = Field('ip-protocol', 8, {'ethernet-type': [2048, 34525]})
329 IPV4_SRC = Field('ipv4-source', 32, {'ethernet-type': [2048]}, value_type=type_ipv4)
330 IPV4_DST = Field('ipv4-destination', 32, {'ethernet-type': [2048]}, value_type=type_ipv4)
332 TCP_SRC = Field('tcp-source-port', 16, {'ip-protocol': [6]})
333 TCP_DST = Field('tcp-destination-port', 16, {'ip-protocol': [6]})
334 UDP_SRC = Field('udp-source-port', 16, {'ip-protocol': [17]})
335 UDP_DST = Field('udp-destination-port', 16, {'ip-protocol': [17]})
336 SCTP_SRC = Field('sctp-source-port', 16, {'ip-protocol': [132]})
337 SCTP_DST = Field('sctp-destination-port', 16, {'ip-protocol': [132]})
338 ICMPV4_TYPE = Field('icmpv4-type', 8, {'ip-protocol': [1]})
339 ICMPV4_CODE = Field('icmpv4-code', 8, {'ip-protocol': [1]})
341 ARP_OP = Field('arp-op', 16, {'ethernet-type': [2054]})
342 ARP_SPA = Field('arp-source-transport-address', 32, {'ethernet-type': [2054]}, value_type=type_ipv4)
343 ARP_TPA = Field('arp-target-transport-address', 32, {'ethernet-type': [2054]}, value_type=type_ipv4)
344 ARP_SHA = Field('arp-source-hardware-address', 48, {'ethernet-type': [2054]}, value_type=type_ethernet)
345 ARP_THA = Field('arp-target-hardware-address', 48, {'ethernet-type': [2054]}, value_type=type_ethernet)
347 IPV6_SRC = Field('ipv6-source', 128, {'ethernet-type': [34525]}, value_type=type_ipv6)
348 IPV6_DST = Field('ipv6-destination', 128, {'ethernet-type': [34525]}, value_type=type_ipv6)
349 IPV6_FLABEL = Field('ipv6-flabel', 20, {'ethernet-type': [34525]})
351 ICMPV6_TYPE = Field('icmpv6-type', 8, {'ip-protocol': [58]})
352 ICMPV6_CODE = Field('icmpv6-code', 8, {'ip-protocol': [58]})
354 IPV6_ND_TARGET = Field('ipv6-nd-target', 128, {'icmpv6-type': [135, 136]}, value_type=type_ipv6)
355 IPV6_ND_SLL = Field('ipv6-nd-sll', 48, {'icmpv6-type': [135]}, value_type=type_ethernet)
356 IPV6_ND_TLL = Field('ipv6-nd-tll', 48, {'icmpv6-type': [136]}, value_type=type_ethernet)
358 MPLS_LABEL = Field('mpls-label', 20, {'ethernet-type': [34887, 34888]})
359 MPLS_TC = Field('mpls-tc', 3, {'ethernet-type': [34887, 34888]})
360 MPLS_BOS = Field('mpls-bos', 1, {'ethernet-type': [34887, 34888]})
362 PBB_ISID = Field('pbb-isid', 24, {'ethernet-type': [35047]})
363 TUNNEL_ID = Field('tunnel-id', 64)
364 IPV6_EXTHDR = Field('ipv6-exthdr', 9, {'ethernet-type': [34525]})
368 def fill_validator(validator):
370 @type validator XMLValidator
373 validator.add_field(Matchers.IN_PORT)
374 validator.add_field(Matchers.IN_PHY_PORT)
375 validator.add_field(Matchers.METADATA)
376 validator.add_field(Matchers.ETH_DST)
377 validator.add_field(Matchers.ETH_SRC)
378 validator.add_field(Matchers.ETH_TYPE)
379 #validator.add_field(Matchers.VLAN_VID) - incorrenct XML parsing, if vlan-id-present is present its overriden by it, need to fix loader
380 validator.add_field(Matchers.VLAN_PCP)
381 validator.add_field(Matchers.IP_DSCP)
382 validator.add_field(Matchers.IP_ENC)
383 validator.add_field(Matchers.IP_PROTO)
384 validator.add_field(Matchers.IPV4_SRC)
385 validator.add_field(Matchers.IPV4_DST)
386 validator.add_field(Matchers.TCP_SRC)
387 validator.add_field(Matchers.TCP_DST)
388 validator.add_field(Matchers.UDP_SRC)
389 validator.add_field(Matchers.UDP_DST)
390 validator.add_field(Matchers.SCTP_SRC)
391 validator.add_field(Matchers.SCTP_DST)
392 validator.add_field(Matchers.ICMPV4_TYPE)
393 validator.add_field(Matchers.ICMPV4_CODE)
394 validator.add_field(Matchers.ARP_OP)
395 validator.add_field(Matchers.ARP_SPA)
396 validator.add_field(Matchers.ARP_TPA)
397 validator.add_field(Matchers.ARP_SHA)
398 validator.add_field(Matchers.ARP_THA)
399 validator.add_field(Matchers.IPV6_SRC)
400 validator.add_field(Matchers.IPV6_DST)
401 validator.add_field(Matchers.IPV6_FLABEL)
402 validator.add_field(Matchers.ICMPV6_TYPE)
403 validator.add_field(Matchers.ICMPV6_CODE)
404 validator.add_field(Matchers.IPV6_ND_TARGET)
405 validator.add_field(Matchers.IPV6_ND_SLL)
406 validator.add_field(Matchers.IPV6_ND_TLL)
407 validator.add_field(Matchers.MPLS_LABEL)
408 validator.add_field(Matchers.MPLS_TC)
409 validator.add_field(Matchers.MPLS_BOS)
410 validator.add_field(Matchers.PBB_ISID)
411 validator.add_field(Matchers.TUNNEL_ID)
412 validator.add_field(Matchers.IPV6_EXTHDR)
415 if __name__ == '__main__':
418 with open(os.path.abspath(__file__ + '/../../../keywords.csv')) as f:
419 keywords = dict(line.strip().split(';') for line in f if not line.startswith('#'))
423 match_keywords = None
424 with open(os.path.abspath(__file__ + '/../../../match-keywords.csv')) as f:
425 match_keywords = dict(line.strip().split(';') for line in f if not line.startswith('#'))
427 #print match_keywords
429 action_keywords = None
430 with open(os.path.abspath(__file__ + '/../../../action-keywords.csv')) as f:
431 action_keywords = dict(line.strip().split(';') for line in f if not line.startswith('#'))
433 paths_to_xml = list()
434 for i in range(1, 50):
435 #paths_to_xml = ['xmls/f5.xml', 'xmls/f14.xml', 'xmls/f23.xml', 'xmls/f25.xml']
436 paths_to_xml.append('xmls/f%d.xml' % i)
438 validator = XMLValidator(keywords, action_keywords, match_keywords, logging.ERROR)
439 validator.fill_fields()
441 for path in paths_to_xml:
442 validator.create_dictionaries(path)