Open Kilda Java Documentation
flow_utils.py
Go to the documentation of this file.
1 # Copyright 2017 Telstra Open Source
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 
16 import os
17 import json
18 import db
19 import copy
20 import calendar
21 import time
22 import collections
23 
24 import message_utils
25 import logging
26 
27 
28 __all__ = ['graph']
29 
30 
31 graph = db.create_p2n_driver()
32 logger = logging.getLogger(__name__)
33 
34 default_rules = ['0x8000000000000001', '0x8000000000000002',
35  '0x8000000000000003']
36 
37 
38 cookie_flag_forward = 0x4000000000000000
39 cookie_flag_reverse = 0x2000000000000000
40 
41 
42 def is_forward_cookie(cookie):
43  cookie = int(cookie)
44  # trying to distinguish kilda and not kilda produced cookies
45  if cookie & 0xE000000000000000:
46  is_match = cookie & cookie_flag_forward
47  else:
48  is_match = (cookie & 0x0080000000000000) == 0
49  return bool(is_match)
50 
51 
52 def is_reverse_cookie(cookie):
53  cookie = int(cookie)
54  # trying to distinguish kilda and not kilda produced cookies
55  if cookie & 0xE000000000000000:
56  is_match = cookie & cookie_flag_reverse
57  else:
58  is_match = (cookie & 0x0080000000000000) != 0
59  return bool(is_match)
60 
61 
62 def cookie_to_hex(cookie):
63  value = hex(
64  ((cookie ^ 0xffffffffffffffff) + 1) * -1 if cookie < 0 else cookie)
65  if value.endswith("L"):
66  value = value[:-1]
67  return value
68 
69 
70 def is_same_direction(first, second):
71  return ((is_forward_cookie(first) and is_forward_cookie(second))
72  or (is_reverse_cookie(first) and is_reverse_cookie(second)))
73 
74 
75 def choose_output_action(input_vlan_id, output_vlan_id):
76  if not int(input_vlan_id):
77  return "PUSH" if int(output_vlan_id) else "NONE"
78  return "REPLACE" if int(output_vlan_id) else "POP"
79 
80 
81 def get_one_switch_rules(src_switch, src_port, src_vlan, dst_port, dst_vlan,
82  bandwidth, flowid, cookie, meter_id, output_action,
83  **k):
84  return [
85  message_utils.build_one_switch_flow(
86  src_switch, src_port, src_vlan, dst_port, dst_vlan,
87  bandwidth, flowid, output_action, cookie, meter_id)]
88 
89 
90 def get_rules(src_switch, src_port, src_vlan, dst_switch, dst_port, dst_vlan,
91  bandwidth, transit_vlan, flowid, cookie, flowpath, meter_id,
92  output_action, **k):
93 
94  # TODO: Rule creation should migrate closer to path creation .. to do as part of TE / Storm refactor
95  # e.g. assuming a refactor of TE into Storm, and possibly more directly attached to the right storm topology
96  # vs a separate topology, then this logic should be closer to path creation
97  # TODO: We should leverage the sequence number to ensure we install / remove flows in the right order
98  # e.g. for install, go from the end to beginning; for remove, go in opposite direction.
99  nodes = flowpath.get("path")
100  if not nodes:
101  return []
102 
103  flows = []
104 
105  flows.append(message_utils.build_ingress_flow(
106  nodes, src_switch, src_port, src_vlan, bandwidth,
107  transit_vlan, flowid, output_action, cookie, meter_id))
108 
109  for i in range(1, len(nodes)-1, 2):
110  src = nodes[i]
111  dst = nodes[i+1]
112 
113  if src['switch_id'] != dst['switch_id']:
114  msg = 'Found non-paired node in the flowpath: {}'.format(flowpath)
115  logger.error(msg)
116  raise ValueError(msg)
117 
118  segment_cookie = src.get('cookie', cookie)
119 
120  flows.append(message_utils.build_intermediate_flows(
121  src['switch_id'], src['port_no'], dst['port_no'], transit_vlan, flowid,
122  segment_cookie))
123 
124  # Egress flow has cookie of the last segment.
125  egress_flow_cookie = cookie
126  if nodes:
127  egress_flow_cookie = nodes[-1].get('cookie', cookie)
128 
129  flows.append(message_utils.build_egress_flow(
130  nodes, dst_switch, dst_port, dst_vlan,
131  transit_vlan, flowid, output_action, egress_flow_cookie))
132 
133  return flows
134 
135 
136 def build_rules(flow):
137  output_action = choose_output_action(flow['src_vlan'], flow['dst_vlan'])
138  if flow['src_switch'] == flow['dst_switch']:
139  return get_one_switch_rules(output_action=output_action, **flow)
140  return get_rules(output_action=output_action, **flow)
141 
142 
143 def remove_flow(flow, parent_tx=None):
144  """
145  Deletes the flow and its flow segments. Start with flow segments (symmetrical mirror of store_flow).
146  Leverage a parent transaction if it exists, otherwise create / close the transaction within this function.
147 
148  - flowid **AND** cookie are *the* primary keys for a flow:
149  - both the forward and the reverse flow use the same flowid
150 
151  NB: store_flow is used for uni-direction .. whereas flow_id is used both directions .. need cookie to differentiate
152  """
153 
154  logger.info('Remove flow: %s', flow['flowid'])
155  tx = parent_tx if parent_tx else graph.begin()
156  delete_flow_segments(flow, tx)
157  query = "MATCH (:switch)-[f:flow {{ flowid: '{}', cookie: {} }}]->(:switch) DELETE f".format(flow['flowid'], flow['cookie'])
158  result = tx.run(query).data()
159  if not parent_tx:
160  tx.commit()
161  return result
162 
163 
164 def merge_flow_relationship(flow_data, tx=None):
165  """
166  This function focuses on just creating the starting/ending switch relationship for a flow.
167  """
168  query = (
169  "MERGE " # MERGE .. create if doesn't exist .. being cautious
170  " (src:switch {{name:'{src_switch}'}}) "
171  " ON CREATE SET src.state = 'inactive' "
172  "MERGE "
173  " (dst:switch {{name:'{dst_switch}'}}) "
174  " ON CREATE SET dst.state = 'inactive' "
175  "MERGE (src)-[f:flow {{" # Should only use the relationship primary keys in a match
176  " flowid:'{flowid}', "
177  " cookie: {cookie} }} ]->(dst) "
178  "SET "
179  " f.meter_id = {meter_id}, "
180  " f.bandwidth = {bandwidth}, "
181  " f.ignore_bandwidth = {ignore_bandwidth}, "
182  " f.src_port = {src_port}, "
183  " f.dst_port = {dst_port}, "
184  " f.src_switch = '{src_switch}', "
185  " f.dst_switch = '{dst_switch}', "
186  " f.src_vlan = {src_vlan}, "
187  " f.dst_vlan = {dst_vlan}, "
188  " f.transit_vlan = {transit_vlan}, "
189  " f.description = '{description}', "
190  " f.last_updated = '{last_updated}', "
191  " f.flowpath = '{flowpath}' "
192  )
193  flow_data['flowpath'].pop('clazz', None) # don't store the clazz info, if it is there.
194  flow_data['last_updated'] = calendar.timegm(time.gmtime())
195  flow_data['flowpath'] = json.dumps(flow_data['flowpath'])
196  if tx:
197  tx.run(query.format(**flow_data))
198  else:
199  graph.run(query.format(**flow_data))
200 
201 
202 def merge_flow_segments(_flow, tx=None):
203  """
204  This function creates each segment relationship in a flow, and then it calls the function to
205  update bandwidth. This should always be down when creating/merging flow segments.
206 
207  To create segments, we leverages the flow path .. and the flow path is a series of nodes, where
208  each 2 nodes are the endpoints of an ISL.
209  """
210  flow = copy.deepcopy(_flow)
211  create_segment_query = (
212  "MERGE " # MERGE .. create if doesn't exist .. being cautious
213  "(src:switch {{name:'{src_switch}'}}) "
214  "ON CREATE SET src.state = 'inactive' "
215  "MERGE "
216  "(dst:switch {{name:'{dst_switch}'}}) "
217  "ON CREATE SET dst.state = 'inactive' "
218  "MERGE "
219  "(src)-[fs:flow_segment {{flowid: '{flowid}', parent_cookie: {parent_cookie} }}]->(dst) "
220  "SET "
221  "fs.cookie = {cookie}, "
222  "fs.src_switch = '{src_switch}', "
223  "fs.src_port = {src_port}, "
224  "fs.dst_switch = '{dst_switch}', "
225  "fs.dst_port = {dst_port}, "
226  "fs.seq_id = {seq_id}, "
227  "fs.segment_latency = {segment_latency}, "
228  "fs.bandwidth = {bandwidth}, "
229  "fs.ignore_bandwidth = {ignore_bandwidth} "
230  )
231 
232  flow_path = get_flow_path(flow)
233  flow_cookie = flow['cookie']
234  flow['parent_cookie'] = flow_cookie # primary key of parent is flowid & cookie
235  logger.debug('MERGE Flow Segments : %s [path: %s]', flow['flowid'], flow_path)
236 
237  for i in range(0, len(flow_path), 2):
238  src = flow_path[i]
239  dst = flow_path[i+1]
240  # <== SRC
241  flow['src_switch'] = src['switch_id']
242  flow['src_port'] = src['port_no']
243  flow['seq_id'] = src['seq_id']
244  # Ignore latency if not provided
245  flow['segment_latency'] = src.get('segment_latency', 'NULL')
246  # ==> DEST
247  flow['dst_switch'] = dst['switch_id']
248  flow['dst_port'] = dst['port_no']
249  # Allow for per segment cookies .. see if it has one set .. otherwise use the cookie of the flow
250  # NB: use the "dst cookie" .. since for flow segments, the delete rule will use the dst switch
251  flow['cookie'] = dst.get('cookie', flow_cookie)
252 
253  # TODO: Preference for transaction around the entire delete
254  # TODO: Preference for batch command
255  if tx:
256  tx.run(create_segment_query.format(**flow))
257  else:
258  graph.run(create_segment_query.format(**flow))
259 
261 
262 
263 def get_flow_path(flow):
264  """
265  As commented elsewhere, current algorithm for flow path is to use both endpoints of a segment, each as their own
266  node. So, make sure we have an even number of them.
267  """
268  flow_path = flow['flowpath']['path']
269  if len(flow_path) % 2 != 0:
270  # The current implementation puts 2 nodes per segment .. throw an error if this changes
271  msg = 'Found un-even number of nodes in the flowpath: {}'.format(flow_path)
272  logger.error(msg)
273  raise ValueError(msg)
274  return flow_path
275 
276 
277 def delete_flow_segments(flow, tx=None):
278  """
279  Whenever adjusting flow segments, always update available bandwidth. Even when creating a flow
280  where we might remove anything old and then create the new .. it isn't guaranteed that the
281  old segments are the same as the new segements.. so update bandwidth to be save.
282  """
283  flow_path = get_flow_path(flow)
284  flowid = flow['flowid']
285  parent_cookie = flow['cookie']
286  logger.debug('DELETE Flow Segments : flowid: %s parent_cookie: 0x%x [path: %s]', flowid, parent_cookie, flow_path)
287  delete_segment_query = (
288  "MATCH (:switch)-[fs:flow_segment {{ flowid: '{}', parent_cookie: {} }}]->(:switch) DELETE fs"
289  )
290  if tx:
291  tx.run(delete_segment_query.format(flowid, parent_cookie))
292  else:
293  graph.run(delete_segment_query.format(flowid, parent_cookie))
295 
296 
297 def fetch_flow_segments(flowid, parent_cookie):
298  """
299  :param flowid: the ID for the entire flow, typically consistent across updates, whereas the cookie may change
300  :param parent_cookie: the cookie for the flow as a whole; individual segments may vary
301  :return: array of segments
302  """
303  fetch_query = (
304  "MATCH (:switch)-[fs:flow_segment {{ flowid: '{}',parent_cookie: {} }}]->(:switch) RETURN fs ORDER BY fs.seq_id"
305  )
306  # This query returns type py2neo.types.Relationship .. it has a dict method to return the properties
307  result = graph.run(fetch_query.format(flowid, parent_cookie)).data()
308  return [dict(x['fs']) for x in result]
309 
310 
312  flow_path = get_flow_path(flow)
313  logger.debug('Update ISL Bandwidth from Flow Segments : %s [path: %s]', flow['flowid'], flow_path)
314  # TODO: Preference for transaction around the entire delete
315  # TODO: Preference for batch command
316  for i in range(0, len(flow_path), 2):
317  src = flow_path[i]
318  dst = flow_path[i+1]
319  update_isl_bandwidth(src['switch_id'], src['port_no'], dst['switch_id'], dst['port_no'], tx)
320 
321 
322 def update_isl_bandwidth(src_switch, src_port, dst_switch, dst_port, tx=None):
323  """
324  This will update the available_bandwidth for the isl that matches the src/dst information.
325  It does this by looking for all flow segments over the ISL, where ignore_bandwidth = false.
326  Because there may not be any segments, have to use "OPTIONAL MATCH"
327  """
328  # print('Update ISL Bandwidth from %s:%d --> %s:%d' % (src_switch, src_port, dst_switch, dst_port))
329 
330  available_bw_query = (
331  "MATCH (src:switch {{name:'{src_switch}'}}), (dst:switch {{name:'{dst_switch}'}}) WITH src,dst "
332  " MATCH (src)-[i:isl {{ src_port:{src_port}, dst_port: {dst_port}}}]->(dst) WITH src,dst,i "
333  " OPTIONAL MATCH (src)-[fs:flow_segment {{ src_port:{src_port}, dst_port: {dst_port}, ignore_bandwidth: false }}]->(dst) "
334  " WITH sum(fs.bandwidth) AS used_bandwidth, i as i "
335  " SET i.available_bandwidth = i.max_bandwidth - used_bandwidth "
336  )
337 
338  logger.debug('Update ISL Bandwidth from %s:%d --> %s:%d' % (src_switch, src_port, dst_switch, dst_port))
339  params = {
340  'src_switch': src_switch,
341  'src_port': src_port,
342  'dst_switch': dst_switch,
343  'dst_port': dst_port,
344  }
345  query = available_bw_query.format(**params)
346  if tx:
347  tx.run(query)
348  else:
349  graph.run(query)
350 
351 
352 def store_flow(flow, tx=None):
353  """
354  Create a :flow relationship between the starting and ending switch, as well as
355  create :flow_segment relationships between every switch in the path.
356 
357  NB: store_flow is used for uni-direction .. whereas flow_id is used both directions .. need cookie to differentiate
358 
359  :param flow:
360  :param tx: The transaction to use, or no transaction.
361  :return:
362  """
363  # TODO: Preference for transaction around the entire set of store operations
364 
365  logger.debug('STORE Flow : %s', flow['flowid'])
366  delete_flow_segments(flow, tx)
367  merge_flow_relationship(copy.deepcopy(flow), tx)
368  merge_flow_segments(flow, tx)
369 
370 
371 def hydrate_flow(one_row):
372  """
373  :param one_row: The typical result from query - ie MATCH (a:switch)-[r:flow]->(b:switch) RETURN r
374  :return: a fully dict'd object
375  """
376  path = json.loads(one_row['r']['flowpath'])
377  flow = json.loads(json.dumps(one_row['r'],
378  default=lambda o: o.__dict__,
379  sort_keys=True))
380  path.setdefault('clazz', 'org.openkilda.messaging.info.event.PathInfoData')
381  flow['flowpath'] = path
382  return flow
383 
384 
385 def get_old_flow(new_flow):
386  query = (
387  "MATCH (a:switch)-[r:flow {{flowid: '{}'}}]->(b:switch) "
388  " WHERE r.cookie <> {} RETURN r "
389  )
390  old_flows = graph.run(query.format(
391  new_flow['flowid'], int(new_flow['cookie']))).data()
392 
393  if not old_flows:
394  message = 'Flow {} not found'.format(new_flow['flowid'])
395  logger.error(message)
396  # TODO (aovchinnikov): replace with specific exception.
397  raise Exception(message)
398  else:
399  logger.info('Flows were found: %s', old_flows)
400 
401  for data in old_flows:
402  old_flow = hydrate_flow(data)
403  logger.info('check cookies: %s ? %s',
404  new_flow['cookie'], old_flow['cookie'])
405  if is_same_direction(new_flow['cookie'], old_flow['cookie']):
406  logger.info('Flow was found: flow=%s', old_flow)
407  return dict(old_flow)
408 
409  # FIXME(surabujin): use custom exception!!!
410  raise Exception(
411  'Requested flow {}(cookie={}) don\'t found corresponding flow (with '
412  'matching direction in Neo4j)'.format(
413  new_flow['flowid'], new_flow['cookie']))
414 
415 
416 # Note this methods is used for LCM functionality. Adds CACHED state to the flow
417 def get_flows():
418  flows = {}
419  query = "MATCH (a:switch)-[r:flow]->(b:switch) RETURN r"
420  try:
421  result = graph.run(query).data()
422 
423  for data in result:
424  flow = hydrate_flow(data)
425  flow['state'] = 'CACHED'
426  flow_pair = flows.get(flow['flowid'], {})
427  if is_forward_cookie(flow['cookie']):
428  flow_pair['forward'] = flow
429  else:
430  flow_pair['reverse'] = flow
431  flows[flow['flowid']] = flow_pair
432 
433  logger.info('Got flows: %s', flows.values())
434  except Exception as e:
435  logger.exception('"Can not get flows: %s', e.message)
436  raise
437  return flows.values()
438 
439 
440 def precreate_switches(tx, *nodes):
441  switches = [x.lower() for x in nodes]
442  switches.sort()
443 
444  for dpid in switches:
445  q = (
446  "MERGE (sw:switch {{name:'{}'}}) "
447  "ON CREATE SET sw.state = 'inactive' "
448  "ON MATCH SET sw.tx_override_workaround = 'dummy'").format(dpid)
449  logger.info('neo4j-query: %s', q)
450  tx.run(q)
451 
452 
454  query = "MATCH p = (:switch)-[fs:flow_segment]->(sw:switch) " \
455  "WHERE sw.name='{}' " \
456  "RETURN fs"
457  result = graph.run(query.format(switch_id)).data()
458 
459  # group flow_segments by parent cookie, it is helpful for building
460  # transit switch rules
461  segments = []
462  for relationship in result:
463  segments.append(relationship['fs'])
464 
465  logger.debug('Found segments for switch %s: %s', switch_id, segments)
466 
467  return segments
468 
469 
470 def get_flows_by_src_switch(switch_id):
471  query = "MATCH (sw:switch)-[r:flow]->(:switch) " \
472  "WHERE sw.name='{}' RETURN r"
473  result = graph.run(query.format(switch_id)).data()
474 
475  flows = []
476  for item in result:
477  flows.append(hydrate_flow(item))
478 
479  logger.debug('Found flows for switch %s: %s', switch_id, flows)
480 
481  return flows
482 
483 
484 def validate_switch_rules(switch_id, switch_rules):
485  """
486  Perform validation of provided rules against the switch flows.
487  """
488 
489  switch_cookies = [x['cookie'] for x in switch_rules]
490 
491  # define three types of result rules
492  missing_rules = set()
493  excess_rules = set()
494  proper_rules = set()
495 
496  # check whether the switch has segments' cookies
497  flow_segments = get_flow_segments_by_dst_switch(switch_id)
498  for segment in flow_segments:
499  cookie = segment.get('cookie', segment['parent_cookie'])
500 
501  if cookie not in switch_cookies:
502  logger.warn('Rule %s is not found on switch %s', cookie, switch_id)
503  missing_rules.add(cookie)
504  else:
505  proper_rules.add(cookie)
506 
507  # check whether the switch has ingress flows (as well as one-switch flows).
508  ingress_flows = get_flows_by_src_switch(switch_id)
509  for flow in ingress_flows:
510  cookie = flow['cookie']
511 
512  if cookie not in switch_cookies:
513  logger.warn("Ingress or one-switch flow %s is missing on switch %s", cookie, switch_id)
514  missing_rules.add(cookie)
515  else:
516  proper_rules.add(cookie)
517 
518  # check whether the switch has redundant rules, skipping the default rules.
519  for cookie in switch_cookies:
520  if cookie not in proper_rules and cookie_to_hex(cookie) not in default_rules:
521  logger.warn('Rule %s is excessive on the switch %s', cookie, switch_id)
522  excess_rules.add(cookie)
523 
524  level = logging.INFO
525  if missing_rules:
526  level = logging.ERROR
527  elif excess_rules:
528  level = logging.WARN
529 
530  diff = {
531  "missing_rules": missing_rules,
532  "excess_rules": excess_rules,
533  "proper_rules": proper_rules}
534 
535  logger.log(level, 'Switch %s rules validation result: %s', switch_id, diff)
536 
537  return diff
538 
539 
540 def build_commands_to_sync_rules(switch_id, switch_rules):
541  """
542  Build install commands to sync provided rules with the switch flows.
543  """
544 
545  installed_rules = set()
546  commands = []
547 
548  flow_segments = get_flow_segments_by_dst_switch(switch_id)
549  for segment in flow_segments:
550  cookie = segment.get('cookie', segment['parent_cookie'])
551 
552  if cookie in switch_rules:
553  logger.info('Rule %s is to be (re)installed on switch %s', cookie, switch_id)
554  installed_rules.add(cookie)
555  commands.extend(build_install_command_from_segment(segment))
556 
557  ingress_flows = get_flows_by_src_switch(switch_id)
558  for flow in ingress_flows:
559  cookie = flow['cookie']
560 
561  if cookie in switch_rules:
562  installed_rules.add(cookie)
563 
564  output_action = choose_output_action(flow['src_vlan'], flow['dst_vlan'])
565 
566  # check if the flow is one-switch flow
567  if flow['src_switch'] == flow['dst_switch']:
568  logger.info("One-switch flow %s is to be (re)installed on switch %s", cookie, switch_id)
569  commands.append(message_utils.build_one_switch_flow_from_db(switch_id, flow, output_action))
570  else:
571  logger.info("Ingress flow %s is to be (re)installed on switch %s", cookie, switch_id)
572  commands.append(message_utils.build_ingress_flow_from_db(flow, output_action))
573 
574  return {"commands": commands, "installed_rules": installed_rules}
575 
576 
578  """
579  Build a command to install required rules for the segment destination.
580  """
581 
582  # check if the flow is one-switch flow
583  if segment['src_switch'] == segment['dst_switch']:
584  msg = 'One-switch flow segment {} is provided.'.format(segment)
585  logger.error(msg)
586  raise ValueError(msg)
587 
588  parent_cookie = segment['parent_cookie']
589  flow_id = segment['flowid']
590  flow = get_flow_by_id_and_cookie(flow_id, parent_cookie)
591  if flow is None:
592  logger.error("Flow with id %s was not found, cookie %s",
593  flow_id, parent_cookie)
594  return
595 
596  output_action = choose_output_action(flow['src_vlan'], flow['dst_vlan'])
597  switch_id = segment['dst_switch']
598  segment_cookie = segment['cookie']
599 
600  # check if the switch is the destination of the flow
601  if switch_id == flow['dst_switch']:
602  yield message_utils.build_egress_flow_from_db(flow, output_action, segment_cookie)
603  else:
604  in_port = segment['dst_port']
605 
606  paired_segment = get_flow_segment_by_src_switch_and_cookie(switch_id, parent_cookie)
607  if paired_segment is None:
608  msg = 'Paired segment for switch {} and cookie {} has not been found.'.format(switch_id, parent_cookie)
609  logger.error(msg)
610  raise ValueError(msg)
611 
612  out_port = paired_segment['src_port']
613 
614  yield message_utils.build_intermediate_flows(
615  switch_id, in_port, out_port, flow['transit_vlan'],
616  flow['flowid'], segment_cookie)
617 
618 
619 def get_flow_by_id_and_cookie(flow_id, cookie):
620  query = "MATCH ()-[r:flow]->() WHERE r.flowid='{}' " \
621  "and r.cookie={} RETURN r"
622  result = graph.run(query.format(flow_id, cookie)).data()
623  if not result:
624  return
625 
626  flow = hydrate_flow(result[0])
627  logger.debug('Found flow for id %s and cookie %s: %s', flow_id, cookie, flow)
628  return flow
629 
630 
631 def get_flow_segment_by_src_switch_and_cookie(switch_id, parent_cookie):
632  query = "MATCH p = (sw:switch)-[fs:flow_segment]->(:switch) " \
633  "WHERE sw.name='{}' AND fs.parent_cookie={} " \
634  "RETURN fs"
635  result = graph.run(query.format(switch_id, parent_cookie)).data()
636  if not result:
637  return
638 
639  segment = result[0]['fs']
640  logger.debug('Found segment for switch %s and parent_cookie %s: %s', switch_id, parent_cookie, segment)
641  return segment
642 
def get_rules(src_switch, src_port, src_vlan, dst_switch, dst_port, dst_vlan, bandwidth, transit_vlan, flowid, cookie, flowpath, meter_id, output_action, k)
Definition: flow_utils.py:92
def merge_flow_relationship(flow_data, tx=None)
Definition: flow_utils.py:164
def delete_flow_segments(flow, tx=None)
Definition: flow_utils.py:277
def get(section, option)
Definition: config.py:43
def get_one_switch_rules(src_switch, src_port, src_vlan, dst_port, dst_vlan, bandwidth, flowid, cookie, meter_id, output_action, k)
Definition: flow_utils.py:83
def is_reverse_cookie(cookie)
Definition: flow_utils.py:52
def store_flow(flow, tx=None)
Definition: flow_utils.py:352
def update_isl_bandwidth(src_switch, src_port, dst_switch, dst_port, tx=None)
Definition: flow_utils.py:322
def is_same_direction(first, second)
Definition: flow_utils.py:70
def update_flow_segment_available_bw(flow, tx=None)
Definition: flow_utils.py:311
def precreate_switches(tx, nodes)
Definition: flow_utils.py:440
def choose_output_action(input_vlan_id, output_vlan_id)
Definition: flow_utils.py:75
def remove_flow(flow, parent_tx=None)
Definition: flow_utils.py:143
def is_forward_cookie(cookie)
Definition: flow_utils.py:42
def build_commands_to_sync_rules(switch_id, switch_rules)
Definition: flow_utils.py:540
def validate_switch_rules(switch_id, switch_rules)
Definition: flow_utils.py:484
def get_flow_by_id_and_cookie(flow_id, cookie)
Definition: flow_utils.py:619
def get_flows_by_src_switch(switch_id)
Definition: flow_utils.py:470
def merge_flow_segments(_flow, tx=None)
Definition: flow_utils.py:202
def fetch_flow_segments(flowid, parent_cookie)
Definition: flow_utils.py:297
def get_old_flow(new_flow)
Definition: flow_utils.py:385
def get_flow_segment_by_src_switch_and_cookie(switch_id, parent_cookie)
Definition: flow_utils.py:631
def build_install_command_from_segment(segment)
Definition: flow_utils.py:577
def get_flow_segments_by_dst_switch(switch_id)
Definition: flow_utils.py:453