+from sfa.util.faults import InvalidRSpec
+from sfa.util.sfalogging import logger
+
+def merge_rspecs(rspecs):
+ """
+ Merge merge a list of RSpecs into 1 RSpec, and return the result.
+ rspecs must be a valid RSpec string or list of RSpec strings.
+ """
+ if not rspecs or not isinstance(rspecs, list):
+ return rspecs
+
+ # ugly hack to avoid sending the same info twice, when the call graph has dags
+ known_networks={}
+ def register_network (network):
+ try:
+ known_networks[network.get('name')]=True
+ except:
+ logger.error("merge_rspecs: cannot register network with no name in rspec")
+ pass
+ def is_registered_network (network):
+ try:
+ return network.get('name') in known_networks
+ except:
+ logger.error("merge_rspecs: cannot retrieve network with no name in rspec")
+ return False
+
+ # the resulting tree
+ rspec = None
+ for input_rspec in rspecs:
+ # ignore empty strings as returned with used call_ids
+ if not input_rspec: continue
+ try:
+ tree = etree.parse(StringIO(input_rspec))
+ except etree.XMLSyntaxError:
+ # consider failing silently here
+ logger.log_exc("merge_rspecs, parse error")
+ message = str(sys.exc_info()[1]) + ' with ' + input_rspec
+ raise InvalidRSpec(message)
+
+ root = tree.getroot()
+ if not root.get("type") in ["SFA"]:
+ logger.error("merge_rspecs: unexpected type for rspec root, %s"%root.get('type'))
+ continue
+ if rspec == None:
+ # we scan the first input, register all networks
+ # in addition we remove duplicates - needed until everyone runs 1.0-10
+ rspec = root
+ for network in root.iterfind("./network"):
+ if not is_registered_network(network):
+ register_network(network)
+ else:
+ # duplicate in the first input - trash it
+ root.remove(network)
+ else:
+ for network in root.iterfind("./network"):
+ if not is_registered_network(network):
+ rspec.append(deepcopy(network))
+ register_network(network)
+ for request in root.iterfind("./request"):
+ rspec.append(deepcopy(request))
+ return etree.tostring(rspec, xml_declaration=True, pretty_print=True)
+