+++ /dev/null
-'''
-Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
-
-This program and the accompanying materials are made available under the
-terms of the Eclipse Public License v1.0 which accompanies this distribution,
-and is available at http://www.eclipse.org/legal/epl-v10.html
-
-Created on May 21, 2014
-
-@author: <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
-'''
-from xml.dom.minidom import Element
-import ipaddr
-import xml.dom.minidom as md
-import copy
-
-KEY_NOT_FOUND = '<KEY_NOT_FOUND>' # KeyNotFound for dictDiff
-
-
-class XMLtoDictParserTools():
-
- @staticmethod
- def parseTreeToDict(node, returnedDict=None, ignoreList=[]):
- """
- Return Dictionary representation of the xml Tree DOM Element.
- Repeated tags are put to the array sorted by key (id or order)
- otherwise is the value represented by tag key name.
- @param node: DOM Element
- @param returnedDict : dictionary (default value None)
- @param ignereList : list of ignored tags for the xml Tree DOM Element
- (default value is empty list)
- @return: dict representation for the input DOM Element
- """
- returnedDict = {} if returnedDict is None else returnedDict
- if (node.nodeType == Element.ELEMENT_NODE):
- nodeKey = (node.localName).encode('utf-8', 'ignore')
- if nodeKey not in ignoreList:
- if node.childNodes is not None:
- childDict = {}
- for child in node.childNodes:
- if child.nodeType == Element.TEXT_NODE:
- nodeValue = (child.nodeValue).encode('utf-8', 'ignore')
- if (len(nodeValue.strip(' \t\n\r'))) > 0:
- XMLtoDictParserTools.addDictValue(returnedDict, nodeKey, nodeValue)
- nodeKey = None
- break
- elif child.nodeType == Element.ELEMENT_NODE:
- childDict = XMLtoDictParserTools.parseTreeToDict(child, childDict, ignoreList)
-
- XMLtoDictParserTools.addDictValue(returnedDict, nodeKey, childDict)
-
- return returnedDict
-
- @staticmethod
- def addDictValue(m_dict, key, value):
-
- def _allign_address(value):
- """unifies output"""
- n = ipaddr.IPNetwork(value)
- return '{0}/{1}'.format(n.network.exploded, n.prefixlen)
-
- def _convert_numbers(value):
- if value.startswith("0x"):
- return str(long(value, 16))
- return str(long(value))
-
- if key is not None:
- if (isinstance(value, str)):
- # we need to predict possible differences
- # for same value in upper or lower case
- value = value.lower()
- if key not in m_dict:
- # lets add mask for ips withot mask
- if key in ['ipv4-destination', 'ipv4-source', 'ipv6-destination', 'ipv6-source', 'ipv6-nd-target']:
- nvalue = _allign_address(value)
- m_dict[key] = nvalue
- elif key in ['tunnel-mask', 'type', 'metadata-mask', 'out_port', 'out_group']:
- nvalue = _convert_numbers(value)
- m_dict[key] = nvalue
- else:
- m_dict[key] = value
- else:
- exist_value = m_dict.get(key)
- if (type(exist_value) is dict):
- list_values = [exist_value, value]
- key_for_sort = XMLtoDictParserTools.searchKey(exist_value)
- if key_for_sort is not None:
- list_values = sorted(list_values, key=lambda k: k[key_for_sort])
- m_dict[key] = list_values
- elif (isinstance(exist_value, list)):
- exist_value.append(value)
- list_values = exist_value
- key_for_sort = XMLtoDictParserTools.searchKey(value)
- if key_for_sort is not None:
- list_values = sorted(list_values, key=lambda k: k[key_for_sort])
- m_dict[key] = list_values
- else:
- m_dict[key] += value
-
- @staticmethod
- def searchKey(dictionary):
- """
- Return an order key for the array ordering. OF_13
- allows only two possible kind of the order keys
- 'order' or '*-id'
- @param dictionary: dictionary with data
- @return: the array order key
- """
- subKeyStr = ['-id', 'order']
- for substr in subKeyStr:
- for key in dictionary:
- if key == substr:
- return key
- elif key.endswith(substr):
- return key
- return None
-
- @staticmethod
- def getDifferenceDict(original_dict, responded_dict):
- """
- Return a dict of keys that differ with another config object. If a value is
- not found in one fo the configs, it will be represented by KEY_NOT_FOUND.
- @param original_dict: Fist dictionary to diff.
- @param responded_dict: Second dictionary to diff.
- @return diff: Dict of Key => (original_dict.val, responded_dict.val)
- Dict of Key => (original_key, KEY_NOT_FOUND)
- Dict of Key => (KEY_NOT_FOUNE, original_key)
- """
- diff = {}
- # Check all keys in original_dict dict
- for key in original_dict.keys():
- if key not in responded_dict:
- # missing key in responded dict
- diff[key] = (key, KEY_NOT_FOUND)
- # check values of the dictionaries
- elif (original_dict[key] != responded_dict[key]):
- # values are not the same #
-
- orig_dict_val = original_dict[key]
- resp_dict_val = responded_dict[key]
-
- # check value is instance of dictionary
- if isinstance(orig_dict_val, dict) and isinstance(resp_dict_val, dict):
- sub_dif = XMLtoDictParserTools.getDifferenceDict(orig_dict_val, resp_dict_val)
- if sub_dif:
- diff[key] = sub_dif
-
- # check value is instance of list
- # TODO - > change a basic comparator to compare by id or order
- elif isinstance(orig_dict_val, list) and isinstance(resp_dict_val, list):
- sub_list_diff = {}
- # the list lengths
- orig_i, resp_i = len(orig_dict_val), len(resp_dict_val)
- # define a max iteration length (less from both)
- min_index = orig_i if orig_i < resp_i else resp_i
- for index in range(0, min_index, 1):
- if (orig_dict_val[index] != resp_dict_val[index]):
- sub_list_diff[index] = (orig_dict_val[index], resp_dict_val[index])
- if (orig_i > min_index):
- # original is longer as responded dict
- for index in range(min_index, orig_i, 1):
- sub_list_diff[index] = (orig_dict_val[index], None)
- elif (resp_i > min_index):
- # responded dict is longer as original
- for index in range(min_index, resp_i, 1):
- sub_list_diff[index] = (None, resp_dict_val[index])
- if sub_list_diff:
- diff[key] = sub_list_diff
-
- else:
- diff[key] = (original_dict[key], responded_dict[key])
-
- # Check all keys in responded_dict dict to find missing
- for key in responded_dict.keys():
- if key not in original_dict:
- diff[key] = (KEY_NOT_FOUND, key)
- return diff
-
-IGNORED_TAGS_FOR_OPERATIONAL_COMPARISON = ['id', 'flow-name', 'barrier', 'cookie_mask', 'installHw', 'flags',
- 'strict', 'byte-count', 'duration', 'packet-count', 'in-port',
- 'vlan-id-present', 'out_group', 'out_port', 'hard-timeout', 'idle-timeout',
- 'flow-statistics', 'cookie', 'clear-actions'] # noqa
-
-IGNORED_PATHS_FOR_OC = [(['flow', 'instructions', 'instruction', 'apply-actions', 'action', 'controller-action'], True), # noqa
- (['flow', 'instructions', 'instruction', 'clear-actions', 'action'], False),
- (['flow', 'instructions', 'instruction', 'apply-actions', 'action', 'push-vlan-action', 'vlan-id'], False), # noqa
- (['flow', 'instructions', 'instruction', 'apply-actions', 'action', 'drop-action'], True),
- (['flow', 'instructions', 'instruction', 'apply-actions', 'action', 'flood-action'], True),
- ]
-
-TAGS_TO_ADD_FOR_OC = [(['flow', 'instructions', 'instruction', 'apply-actions', 'action', 'output-action'], 'max-length', '0'), # noqa
- ]
-
-
-TAGS_TO_MODIFY_FOR_OC = [(['flow', 'match', 'metadata'], 'metadata', 'metadata-mask'),
- (['flow', 'match', 'tunnel'], 'tunnel-id', 'tunnel-mask'),
- ]
-
-
-class XmlComparator:
-
- def is_flow_configured(self, requested_flow, configured_flows):
-
- orig_tree = md.parseString(requested_flow)
- xml_resp_stream = configured_flows.encode('utf-8', 'ignore')
- xml_resp_tree = md.parseString(xml_resp_stream)
- nodeListOperFlows = xml_resp_tree.getElementsByTagNameNS("*", 'flow')
- origDict = XMLtoDictParserTools.parseTreeToDict(orig_tree._get_documentElement())
-
- reportDict = {}
- index = 0
- for node in nodeListOperFlows:
- nodeDict = XMLtoDictParserTools.parseTreeToDict(node)
- XMLtoDictParserTools.addDictValue(reportDict, index, nodeDict)
- index += 1
- # print nodeDict
- # print origDict
- if nodeDict == origDict:
- return True, ''
- if nodeDict['flow']['priority'] == origDict['flow']['priority']:
- return False, 'Flow found with diferences {0}'.format(
- XMLtoDictParserTools.getDifferenceDict(nodeDict, origDict))
- return False, ''
-
- def is_flow_operational2(self, requested_flow, oper_resp):
- def _rem_unimplemented_tags(tagpath, recurs, tdict):
- # print "_rem_unimplemented_tags", tagpath, tdict
- if len(tagpath) > 1 and tagpath[0] in tdict:
- _rem_unimplemented_tags(tagpath[1:], recurs, tdict[tagpath[0]])
-
- # when not to delete anything
- if len(tagpath) == 1 and tagpath[0] not in tdict:
- return
- if len(tagpath) == 0:
- return
-
- # when to delete
- if len(tagpath) == 1 and tagpath[0] in tdict:
- del tdict[tagpath[0]]
- if len(tagpath) > 1 and recurs is True and tagpath[0] in tdict and tdict[tagpath[0]] == {}:
- del tdict[tagpath[0]]
- if tdict.keys() == ['order']:
- del tdict['order']
- # print "leaving", tdict
-
- def _add_tags(tagpath, newtag, value, tdict):
- '''if whole tagpath exists and the tag is not present, it is added with given value'''
- # print "_add_tags", tagpath, newtag, value, tdict
- if len(tagpath) > 0 and tagpath[0] in tdict:
- _add_tags(tagpath[1:], newtag, value, tdict[tagpath[0]])
- elif len(tagpath) == 0 and newtag not in tdict:
- tdict[newtag] = value
-
- def _to_be_modified_tags(tagpath, tag, related_tag, tdict):
- '''if whole tagpath exists and the tag is not present, it is added with given value'''
- # print "_to_be_modified_tags", tagpath, tag, related_tag, tdict
- if len(tagpath) > 0 and tagpath[0] in tdict:
- _to_be_modified_tags(tagpath[1:], tag, related_tag, tdict[tagpath[0]])
- elif len(tagpath) == 0 and tag in tdict and related_tag in tdict:
- tdict[tag] = str(long(tdict[tag]) & long(tdict[related_tag]))
-
- orig_tree = md.parseString(requested_flow)
- xml_resp_stream = oper_resp.encode('utf-8', 'ignore')
- xml_resp_tree = md.parseString(xml_resp_stream)
- nodeListOperFlows = xml_resp_tree.getElementsByTagNameNS("*", 'flow')
- origDict = XMLtoDictParserTools.parseTreeToDict(
- orig_tree._get_documentElement(),
- ignoreList=IGNORED_TAGS_FOR_OPERATIONAL_COMPARISON)
-
- # origDict['flow-statistics'] = origDict.pop( 'flow' )
- reportDict = {}
- index = 0
- for node in nodeListOperFlows:
- nodeDict = XMLtoDictParserTools.parseTreeToDict(
- node,
- ignoreList=IGNORED_TAGS_FOR_OPERATIONAL_COMPARISON)
- XMLtoDictParserTools.addDictValue(reportDict, index, nodeDict)
- index += 1
- # print nodeDict
- # print origDict
- # print reportDict
- if nodeDict == origDict:
- return True, ''
- if nodeDict['flow']['priority'] == origDict['flow']['priority']:
- for p in IGNORED_PATHS_FOR_OC:
- td = copy.copy(origDict)
- _rem_unimplemented_tags(p[0], p[1], td)
- for (p, t, v) in TAGS_TO_ADD_FOR_OC:
- _add_tags(p, t, v, td)
- for (p, t, rt) in TAGS_TO_MODIFY_FOR_OC:
- _to_be_modified_tags(p, t, rt, td)
-
- # print "comparing1", nodeDict
- # print "comparing2", td
- if nodeDict == td:
- return True, ''
- if nodeDict == origDict:
- return True, ''
- return False, 'Flow found with diferences {0}'.format(
- XMLtoDictParserTools.getDifferenceDict(nodeDict, origDict))
- return False, ''
-
- def get_data_for_flow_put_update(self, xml):
- # action only for yet
- xml_dom_input = md.parseString(xml)
- actionList = xml_dom_input.getElementsByTagName('action')
- if actionList is not None and len(actionList) > 0:
- action = actionList[0]
- for child in action.childNodes:
- if child.nodeType == Element.ELEMENT_NODE:
- nodeKey = (child.localName).encode('utf-8', 'ignore')
- if nodeKey != 'order':
- if nodeKey != 'drop-action':
- new_act = child.ownerDocument.createElement('drop-action')
- else:
- new_act = child.ownerDocument.createElement('output-action')
- onc = child.ownerDocument.createElement('output-node-connector')
- onc_content = child.ownerDocument.createTextNode('TABLE')
- onc.appendChild(onc_content)
- new_act.appendChild(onc)
- ml = child.ownerDocument.createElement('max-length')
- ml_content = child.ownerDocument.createTextNode('60')
- ml.appendChild(ml_content)
- new_act.appendChild(ml)
- child.parentNode.replaceChild(new_act, child)
- return xml_dom_input.toxml(encoding='utf-8')
-
- def get_flow_content(self, tid=1, fid=1, priority=1):
- """Returns an xml flow content identified by given details.
-
- Args:
- :param tid: table id
- :param fid: flow id
- :param priority: flow priority
- """
-
- flow_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<flow xmlns="urn:opendaylight:flow:inventory">
- <strict>false</strict>
- <instructions>
- <instruction>
- <order>0</order>
- <apply-actions>
- <action>
- <order>0</order>
- <drop-action/>
- </action>
- </apply-actions>
- </instruction>
- </instructions>
- <table_id>%s</table_id>
- <id>%s</id>
- <cookie_mask>4294967295</cookie_mask>
- <installHw>false</installHw>
- <match>
- <ethernet-match>
- <ethernet-type>
- <type>2048</type>
- </ethernet-type>
- </ethernet-match>
- <ipv4-source>10.0.0.1/32</ipv4-source>
- </match>
- <cookie>%s</cookie>
- <flow-name>%s</flow-name>
- <priority>%s</priority>
- <barrier>false</barrier>
-</flow>'''
-
- flow_data = flow_template % (tid, fid, fid, 'TestFlow-{0}'.format(fid), priority)
- return flow_data