From 2ccb1bc3573de28af2353820a6f56686580ec98a Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 12 Mar 2020 12:40:05 -0400 Subject: [PATCH 01/32] testing _backup_port_recover --- localctlr/RyuTranslateInterface.py | 61 +++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index c298899a..9ee2bee8 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -549,9 +549,12 @@ def _new_switch_bootstrapping(self, ev): # For last table # - Create a default drop rule (if necessary needed). Priority 0 for i in (managementvlanports): + # If port has backup port, use first one as default port + if isinstance(i, (list)): + i = i[0] matches = [IN_PORT(i)] actions = [Drop()] - priorty = PRIORITY_DEFAULT_PLUS_ONE + priority = PRIORITY_DEFAULT_PLUS_ONE table = LASTTABLE marule = MatchActionLCRule(switch_id, matches, actions) results += self._translate_MatchActionLCRule(datapath, @@ -562,7 +565,7 @@ def _new_switch_bootstrapping(self, ev): # Catch-all for those not in the same port matches = [] actions = [Drop()] - priorty = PRIORITY_DEFAULT + priority = PRIORITY_DEFAULT table = LASTTABLE marule = MatchActionLCRule(switch_id, matches, actions) results += self._translate_MatchActionLCRule(datapath, @@ -594,6 +597,60 @@ def _new_switch_bootstrapping(self, ev): for rule in results: self.add_flow(datapath, rule) + def _backup_port_recover(self, ev): + '''Remove existing port and use backup port''' + rule_results = [] + switch_id = 0 + datapath = ev.msg.datapath + of_cookie = self._get_new_OF_cookie(-1) + # Extract management VLAN and ports from the manifest + internal_config = self._get_switch_internal_config(datapath.id) + + if internal_config == None: + raise ValueError("DPID %s does not have internal_config" % + datapath.id) + + if 'managementvlanports' in internal_config.keys(): + managementvlanports = internal_config['managementvlanports'] + + for i in (managementvlanports): + # If port has backup port, use first one as default port + if isinstance(i, (list)): + if len(i) > 1: + i = i[0] + matches = [IN_PORT(i)] + actions = [Drop()] + priority = PRIORITY_DEFAULT_PLUS_ONE + table = LASTTABLE + marule = MatchActionLCRule(switch_id, matches, actions) + rule_results += self._translate_MatchActionLCRule(datapath, + table, + of_cookie, + marule, + priority) + for rule in rule_results: + self.remove_flow(datapath, rule) + + for i in (managementvlanports): + # If port has backup port, use first one as default port + if isinstance(i, (list)): + if len(i) > 1: + i = i[1] + matches = [IN_PORT(i)] + actions = [Drop()] + priority = PRIORITY_DEFAULT_PLUS_ONE + table = LASTTABLE + marule = MatchActionLCRule(switch_id, matches, actions) + rule_results += self._translate_MatchActionLCRule(datapath, + table, + of_cookie, + marule, + priority) + + # Install default rules + for rule in rule_results: + self.add_flow(datapath, rule) + def _translate_MatchActionLCRule(self, datapath, table, of_cookie, marule, priority=100): ''' This translates MatchActionLCRules. There is only one rule generated From 591dbbe5a1cca0950cc62e6f4a6f64f00d22c54e Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 12 Mar 2020 12:50:30 -0400 Subject: [PATCH 02/32] adding renci_ben_backup_port.manifest --- .../renci_ben_backup_port.manifest | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 configuration/renci_testbed/renci_ben_backup_port.manifest diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest new file mode 100644 index 00000000..c015434f --- /dev/null +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -0,0 +1,393 @@ +{ + "endpoints": { + "sdx": { + "type": "sdxcontroller", + "friendlyname": "SDX Controller", + "location": "35.939796, -79.018203", + "vlan": 1411}, + "rencictlr": { + "type": "localcontroller", + "friendlyname": "RENCI Controller", + "location": "35.939796, -79.018203", + "vlan": 1411}, + "rencidtn": { + "type": "dtn", + "friendlyname": "RENCI BF40G Node", + "location": "35.939796, -79.018203", + "vlan": 1421}, + "rencibm8": { + "type": "dtn", + "friendlyname": "RENCI BM8 Node", + "location": "35.939796, -79.018203", + "vlan": 1425}, + "dukectlr": { + "type": "localcontroller", + "friendlyname": "DUKE Controller", + "location": "36.003614, -78.938755", + "vlan": 1411}, + "dukedtn": { + "type": "dtn", + "friendlyname": "DUKE BF40G Node", + "location": "36.003614, -78.938755", + "vlan": 1422}, + "uncctlr": { + "type": "localcontroller", + "friendlyname": "UNC Controller", + "location": "35.903395, -79.048370", + "vlan": 1411}, + "uncdtn": { + "type": "dtn", + "friendlyname": "UNC BF40G Node", + "location": "35.903395, -79.048370", + "vlan": 1423}, + "ncsuctlr": { + "type": "localcontroller", + "friendlyname": "NCSU Controller", + "location": "35.773395, -78.677730", + "vlan": 1411}, + "ncsudtn": { + "type": "dtn", + "friendlyname": "NCSU BF40G Node", + "location": "35.773395, -78.677730", + "vlan": 1424} + }, + "localcontrollers": { + "rencictlr":{ + "shortname": "rencictlr", + "credentials": "rencipwd", + "location": "35.939796, -79.018203", + "lcip": "10.14.11.1", + "internalconfig": { + "ryucxninternalport": 55781, + "openflowport": 6681 + }, + "switchinfo": [ + { + "name":"rencis1", + "friendlyname":"RENCI Corsa DP2100", + "ip": "10.0.201.1", + "dpid":"201", + "brand":"Open vSwitch", + "model": "2.3.0", + "portinfo": [ + { + "portnumber": 1, + "speed": 80000000000, + "destination": "uncs1" + }, + { + "portnumber": 2, + "speed": 80000000000, + "destination": "dukes1" + }, + { + "portnumber": 23, + "speed": 80000000000, + "destination": "rencis2" + }, + { + "portnumber": 11, + "speed": 80000000000, + "destination": "rencictlr" + }, + { + "portnumber": 12, + "speed": 80000000000, + "destination": "rencidtn" + }, + { + "portnumber": 30, + "speed": 80000000000, + "destination": "sdx" + } + ], + "internalconfig": { + "corsaurl": "https://corsa-2.renci.ben/", + "corsatoken": "ddc60638fa7a7aa3fb5ca10de8f4e5e8bf82cd289187f933cfc7d7a01e7f7f3839ecac1145bc9908abfd03aa493e4acda448522b304a6ce779f82ce9f1528356", + "corsabridge": "br21", + "corsabwin": 19, + "corsabwout": 20, + "corsaratelimitbridge": "br20", + "corsaratelimitports": [21,22], + "managementvlan":1411, + "managementvlanports":[1,2,11,23,30] + } + }, + { + "name":"rencis2", + "friendlyname":"RENCI INNO Corsa DP2100", + "ip": "10.0.201.1", + "dpid":"205", + "brand":"Open vSwitch", + "model": "2.3.0", + "portinfo": [ + { + "portnumber": 23, + "speed": 80000000000, + "destination": "rencis1" + }, + { + "portnumber": 8, + "speed": 80000000000, + "destination": "rencibm8" + } + ], + "internalconfig": { + "corsaurl": "https://corsa-1.renci.ben/", + "corsatoken": "245c25eac968cb22624438f0e1bcd2d766be666843676e413b6739d86f6c3523bf7aab1e5c3231aa2d7acd6757371dea3d8cbc470a1e60825f93a85dabd01ddb", + "corsabridge": "br25", + "corsabwin": 25, + "corsabwout": 26, + "corsaratelimitbridge": "br20", + "corsaratelimitports": [27,28], + "managementvlan":1411, + "managementvlanports":[23] + } + } + ], + "operatorinfo": { + "organization": "RENCI", + "administrator": "Mert Cevik", + "contact": "mcevik@renci.org" + } + }, + "dukectlr":{ + "shortname": "dukectlr", + "credentials": "dukepwd", + "location":"36.003614, -78.938755", + "lcip": "10.14.11.2", + "internalconfig": { + "ryucxninternalport": 55782, + "openflowport": 6682 + }, + "switchinfo": [ + { + "name":"dukes1", + "friendlyname":"DUKE Corsa DP2100", + "ip": "10.0.202.1", + "dpid": "202", + "brand": "Open vSwitch", + "model": "2.3.0", + "portinfo": [ + { + "portnumber": 1, + "speed": 80000000000, + "destination": "rencis1" + }, + { + "portnumber": 2, + "speed": 80000000000, + "destination": "ncsus1" + }, + { + "portnumber": 11, + "speed": 80000000000, + "destination": "dukectlr" + }, + { + "portnumber": 12, + "speed": 80000000000, + "destination": "dukedtn" + } + ], + "internalconfig": { + "corsaurl": "https://corsa-1.duke.ben/", + "corsatoken": "c73ad6e29773582187c06a1558f8ecc71ea273b3a5ae9e4f03f153d73f6436a619f3c8471205c28e25f9ae62741d5435e54c8a6f33f2b333154430448dd18215", + "corsabridge": "br22", + "corsabwin":19, + "corsabwout":20, + "corsaratelimitbridge":"br20", + "corsaratelimitports":[21,22], + "managementvlan":1411, + "managementvlanports":[1,2,11] + } + } + ], + "operatorinfo": { + "organization": "RENCI", + "administrator": "Mert Cevik", + "contact": "mcevik@renci.org" + } + }, + "uncctlr":{ + "shortname": "uncctlr", + "credentials": "uncpwd", + "location":"35.903395, -79.048370", + "lcip": "10.14.11.3", + "internalconfig": { + "ryucxninternalport": 55783, + "openflowport": 6683 + }, + "switchinfo": [ + { + "name":"uncs1", + "friendlyname":"UNC Corsa DP2100", + "ip": "10.0.203.1", + "dpid":"203", + "brand":"Open vSwitch", + "model": "2.3.0", + "portinfo": [ + { + "portnumber": 1, + "speed": 80000000000, + "destination": "rencis1" + }, + { + "portnumber": 11, + "speed": 80000000000, + "destination": "uncctlr" + }, + { + "portnumber": 12, + "speed": 80000000000, + "destination": "uncdtn" + } + ], + "internalconfig": { + "corsaurl": "https://corsa-1.unc.ben/", + "corsatoken": "fa18b3d4d84507dba6568678a45fcecdec03247d7b5c18e45c5f288066a52d970cf8ee0bcf7759c698a7b56b92824963c8c03acf77f0a3aa91ad4f64c3aa7b15", + "corsabridge": "br23", + "corsabwin":19, + "corsabwout":20, + "corsaratelimitbridge":"br20", + "corsaratelimitports":[21,22], + "managementvlan":1411, + "managementvlanports":[[1, 2],11] + } + } + ], + "operatorinfo": { + "organization": "RENCI", + "administrator": "Mert Cevik", + "contact": "mcevik@renci.org" + } + }, + "ncsuctlr":{ + "shortname": "ncsuctlr", + "credentials": "ncsupwd", + "location": "35.773395, -78.677730", + "lcip": "10.14.11.4", + "internalconfig": { + "ryucxninternalport": 55784, + "openflowport": 6684 + }, + "switchinfo": [ + { + "name":"ncsus1", + "friendlyname":"NCSU Corsa DP2100", + "ip": "10.0.204.1", + "dpid":"204", + "brand":"Open vSwitch", + "model": "2.3.0", + "portinfo": [ + { + "portnumber": 1, + "speed": 80000000000, + "destination": "dukes1" + }, + { + "portnumber": 11, + "speed": 80000000000, + "destination": "ncsuctlr" + }, + { + "portnumber": 12, + "speed": 80000000000, + "destination": "ncsudtn" + } + ], + "internalconfig": { + "corsaurl": "https://corsa-1.ncsu.ben/", + "corsatoken": "9b95dda0314beb7acf620084dff53e5df7eaf80f9ee453cfb3550f33aecd356561fcf22568ac8365f2892725f129147ceb5718cb711c3a93b136030348dd9eeb", + "corsabridge": "br24", + "corsabwin":19, + "corsabwout":20, + "corsaratelimitbridge":"br20", + "corsaratelimitports":[21,22], + "managementvlan":1411, + "managementvlanports":[1,11] + } + } + ], + "operatorinfo": { + "organization": "RENCI", + "administrator": "Mert Cevik", + "contact": "mcevik@renci.org" + } + } + }, + "participants": { + "sdonovan": { + "credentials": "1234", + "organization": "Georgia Tech/RNOC", + "contact": "sdonovan@gatech.edu", + "type": "administrator", + "permitted_actions": [ + "tbd" + ], + "restrictions": [ + "tbd" + ] + }, + "jchung": { + "credentials": "4321", + "organization": "Georgia Tech/RNOC", + "contact": "jchung@gatech.edu", + "type": "user", + "permitted_actions": [ + "tbd" + ], + "restrictions": [ + "tbd" + ] + }, + "cwang": { + "credentials": "1234", + "organization": "RENCI", + "contact": "cwang@renci.org", + "type": "administrator", + "permitted_actions": [ + "tbd" + ], + "restrictions": [ + "tbd" + ] + }, + "yxin": { + "credentials": "1234", + "organization": "RENCI", + "contact": "yxin@renci.org", + "type": "administrator", + "permitted_actions": [ + "tbd" + ], + "restrictions": [ + "tbd" + ] + }, + "stealey": { + "credentials": "1234", + "organization": "RENCI", + "contact": "stealey@renci.org", + "type": "administrator", + "permitted_actions": [ + "tbd" + ], + "restrictions": [ + "tbd" + ] + }, + "mcevik": { + "credentials": "1234", + "organization": "RENCI", + "contact": "mcevik@renci.org", + "type": "administrator", + "permitted_actions": [ + "tbd" + ], + "restrictions": [ + "tbd" + ] + } + } +} From 842124c071ef96e3741f1a59169d29b7d194ad37 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 12 Mar 2020 16:25:16 -0400 Subject: [PATCH 03/32] change backup port format in manifest --- .../renci_ben_backup_port.manifest | 3 ++- localctlr/RyuTranslateInterface.py | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index c015434f..a09f820e 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -252,7 +252,8 @@ "corsaratelimitbridge":"br20", "corsaratelimitports":[21,22], "managementvlan":1411, - "managementvlanports":[[1, 2],11] + "managementvlanports":[1,11] + "managementvlanbackupports":[[1, 2]] } } ], diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index 9ee2bee8..b62d061d 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -550,8 +550,8 @@ def _new_switch_bootstrapping(self, ev): # - Create a default drop rule (if necessary needed). Priority 0 for i in (managementvlanports): # If port has backup port, use first one as default port - if isinstance(i, (list)): - i = i[0] + #if isinstance(i, (list)): + # i = i[0] matches = [IN_PORT(i)] actions = [Drop()] priority = PRIORITY_DEFAULT_PLUS_ONE @@ -610,10 +610,16 @@ def _backup_port_recover(self, ev): raise ValueError("DPID %s does not have internal_config" % datapath.id) - if 'managementvlanports' in internal_config.keys(): - managementvlanports = internal_config['managementvlanports'] + #if 'managementvlanports' in internal_config.keys(): + # managementvlanports = internal_config['managementvlanports'] + + if 'managementvlanbackupports' in internal_config.keys(): + managementvlanbackupports = internal_config['managementvlanbackupports'] + else: + self.logger.debug("No backup port provided") + return - for i in (managementvlanports): + for i in (managementvlanbackupports): # If port has backup port, use first one as default port if isinstance(i, (list)): if len(i) > 1: @@ -631,7 +637,7 @@ def _backup_port_recover(self, ev): for rule in rule_results: self.remove_flow(datapath, rule) - for i in (managementvlanports): + for i in (managementvlanbackupports): # If port has backup port, use first one as default port if isinstance(i, (list)): if len(i) > 1: From 1f8412c2a5b02f2305aa500ff3342b15546a9ccd Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 12 Mar 2020 16:31:09 -0400 Subject: [PATCH 04/32] add log messages --- localctlr/RyuTranslateInterface.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index b62d061d..3f19537b 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -552,6 +552,7 @@ def _new_switch_bootstrapping(self, ev): # If port has backup port, use first one as default port #if isinstance(i, (list)): # i = i[0] + self.logger.debug("Using default management ports.") matches = [IN_PORT(i)] actions = [Drop()] priority = PRIORITY_DEFAULT_PLUS_ONE @@ -599,6 +600,7 @@ def _new_switch_bootstrapping(self, ev): def _backup_port_recover(self, ev): '''Remove existing port and use backup port''' + self.logger.debug("Check if backup port is available") rule_results = [] switch_id = 0 datapath = ev.msg.datapath @@ -634,6 +636,9 @@ def _backup_port_recover(self, ev): of_cookie, marule, priority) + + # Remove default rules + self.logger.debug("Removing default management VLAN port") for rule in rule_results: self.remove_flow(datapath, rule) @@ -653,7 +658,8 @@ def _backup_port_recover(self, ev): marule, priority) - # Install default rules + # Install backup rules + self.logger.debug("Adding new management VLAN port") for rule in rule_results: self.add_flow(datapath, rule) From 4c0a6a6949e52421b4b9cac8c06293eef075ebcb Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 17 Mar 2020 09:17:26 -0400 Subject: [PATCH 05/32] add port status change event handler --- .../renci_testbed/renci_ben_backup_port.manifest | 2 +- localctlr/RyuTranslateInterface.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index a09f820e..ce915c3f 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -252,7 +252,7 @@ "corsaratelimitbridge":"br20", "corsaratelimitports":[21,22], "managementvlan":1411, - "managementvlanports":[1,11] + "managementvlanports":[1,11], "managementvlanbackupports":[[1, 2]] } } diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index 3f19537b..e31c90d1 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -486,6 +486,15 @@ def switch_features_handler(self, ev): # Call bootstrapping for switch functions self._new_switch_bootstrapping(ev) + # Handles port status change event + @set_ev_cls(ofp_event.EventOFPPortStatus, CONFIG_DISPATCHER) + def port_status_handler(self, ev): + self.logger.warning("Port status changed: connection from: " + str(ev.msg.datapath.id) + " for " + str(self)) + self.datapaths[ev.msg.datapath.id] = ev.msg.datapath + + # Call backup port recovery for switch functions + self._backup_port_recover(ev) + # From the Ryu mailing list: https://sourceforge.net/p/ryu/mailman/message/33584125/ @set_ev_cls(ofp_event.EventOFPErrorMsg, [CONFIG_DISPATCHER, MAIN_DISPATCHER]) @@ -637,6 +646,7 @@ def _backup_port_recover(self, ev): marule, priority) + rule_results = [] # Remove default rules self.logger.debug("Removing default management VLAN port") for rule in rule_results: From 3577b96556414f4772708d35454666311de86976 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 23 Mar 2020 10:00:16 -0400 Subject: [PATCH 06/32] trying rule recovery --- localctlr/LCRuleManager.py | 13 +++- localctlr/LocalController.py | 12 +++- localctlr/RyuTranslateInterface.py | 104 +++++++++++++++++++++++++++-- 3 files changed, 120 insertions(+), 9 deletions(-) diff --git a/localctlr/LCRuleManager.py b/localctlr/LCRuleManager.py index a4adba4d..8f4ce033 100644 --- a/localctlr/LCRuleManager.py +++ b/localctlr/LCRuleManager.py @@ -124,7 +124,18 @@ def _find_rules(self, filter={}): pickle.loads(str(x['rule'])), x['status']) for x in results] return retval - + + def list_all_rules(self, switch_id, full_tuple=False): + rules = self.rule_table.find() + self.logger.debug("Retrieving all rules.") + if full_tuple: + retval = [(x['cookie'], + x['switch_id'], + pickle.loads(str(x['rule'])), + x['status']) for x in rules] + return retval + retval = [pickle.loads(str(x['rule'])) for x in rules] + return retval def get_rules(self, cookie, switch_id, full_tuple=False): ''' Returns a list of all rules matching cookie and switch_id. diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index b71d585f..aafe836f 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -105,6 +105,8 @@ def _main_loop(self): wlist = [] xlist = rlist timeout = 1.0 + num_of_retry = 0 + all_rules = [] self.logger.debug("Inside Main Loop, SDX connection: %s" % (self.sdx_connection)) @@ -248,9 +250,15 @@ def _main_loop(self): self.sdx_connection.close() self.sdx_connection = None self.cxn_q.put((DEL_CXN, cxn)) + if num_of_retry <= 5: + # Restart new connection + self.start_sdx_controller_connection() + sleep(5) - # Restart new connection - self.start_sdx_controller_connection() + self.all_rules = self.rm.list_all_rules + num_of_retry = 0 + + def _add_switch_config_to_db(self, switch_name, switch_config): # Pushes a switch info dictionary from manifest. diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index e31c90d1..da066a96 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -558,9 +558,6 @@ def _new_switch_bootstrapping(self, ev): # For last table # - Create a default drop rule (if necessary needed). Priority 0 for i in (managementvlanports): - # If port has backup port, use first one as default port - #if isinstance(i, (list)): - # i = i[0] self.logger.debug("Using default management ports.") matches = [IN_PORT(i)] actions = [Drop()] @@ -607,9 +604,103 @@ def _new_switch_bootstrapping(self, ev): for rule in results: self.add_flow(datapath, rule) + def _backup_port_recover_remove_all(self, ev): + '''Remove existing default port and use backup port''' + self.logger.debug("Checking if backup port is available") + switch_id = 0 # This is unimportant: + # it's never used in the translation + datapath = ev.msg.datapath + + self.remove_all_flows(datapath) + + of_cookie = self._get_new_OF_cookie(-1) # FIXME: magic number + results = [] + + # In-band Communication + # Extract management VLAN and ports from the manifest + internal_config = self._get_switch_internal_config(datapath.id) + if internal_config == None: + raise ValueError("DPID %s does not have internal_config" % + datapath.id) + if 'managementvlan' in internal_config.keys(): + managementvlan = internal_config['managementvlan'] + if 'managementvlanports' in internal_config.keys(): + managementvlanports = internal_config['managementvlanports'] + + if 'managementvlanbackupports' in internal_config.keys(): + managementvlanbackupports = internal_config['managementvlanbackupports'] + else: + self.logger.debug("No backup port provided") + return + + for table in ALL_TABLES_EXCEPT_LAST: + matches = [] # FIXME: what's the equivalent of match(*)? + actions = [Continue()] + priority = PRIORITY_DEFAULT + marule = MatchActionLCRule(switch_id, matches, actions) + results += self._translate_MatchActionLCRule(datapath, + table, + of_cookie, + marule, + priority) + + # For last table + # - Create a default drop rule (if necessary needed). Priority 0 + for i in (managementvlanports): + self.logger.debug("Using default management ports.") + for j in (managementvlanbackupports): + if i == j[0] and len(j) > 1: + i = j[1] + matches = [IN_PORT(i)] + actions = [Drop()] + priority = PRIORITY_DEFAULT_PLUS_ONE + table = LASTTABLE + marule = MatchActionLCRule(switch_id, matches, actions) + results += self._translate_MatchActionLCRule(datapath, + table, + of_cookie, + marule, + priority) + # Catch-all for those not in the same port + matches = [] + actions = [Drop()] + priority = PRIORITY_DEFAULT + table = LASTTABLE + marule = MatchActionLCRule(switch_id, matches, actions) + results += self._translate_MatchActionLCRule(datapath, + table, + of_cookie, + marule, + priority) + + # In-band Communication + # If the management VLAN needs to be setup, set it up. + if 'managementvlan' in internal_config.keys(): + managementvlan = internal_config['managementvlan'] + managementvlanports = internal_config['managementvlanports'] + untaggedmanagementvlanports = [] + if 'untaggedmanagementvlanports' in internal_config.keys(): + untaggedmanagementvlanports = internal_config['untaggedmanagementvlanports'] + + table = L2TUNNELTABLE + mvrule = ManagementVLANLCRule(switch_id, + managementvlan, + managementvlanports, + untaggedmanagementvlanports) + results += self._translate_ManagementVLANLCRule(datapath, + table, + of_cookie, + mvrule) + + # Install default rules + for rule in results: + self.add_flow(datapath, rule) + + + def _backup_port_recover(self, ev): - '''Remove existing port and use backup port''' - self.logger.debug("Check if backup port is available") + '''Remove existing default port and use backup port''' + self.logger.debug("Checking if backup port is available") rule_results = [] switch_id = 0 datapath = ev.msg.datapath @@ -646,12 +737,13 @@ def _backup_port_recover(self, ev): marule, priority) - rule_results = [] # Remove default rules self.logger.debug("Removing default management VLAN port") for rule in rule_results: self.remove_flow(datapath, rule) + rule_results = [] + for i in (managementvlanbackupports): # If port has backup port, use first one as default port if isinstance(i, (list)): From 41702b7080dbccfe73db3c76af449deddf157af4 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 23 Mar 2020 15:32:30 +0000 Subject: [PATCH 07/32] wip --- localctlr/LCRuleManager.py | 4 ++-- localctlr/LocalController.py | 26 ++++++++++++++++++++++++-- shared/FloodTreeLCRule.py | 6 ++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/localctlr/LCRuleManager.py b/localctlr/LCRuleManager.py index 8f4ce033..52e74d67 100644 --- a/localctlr/LCRuleManager.py +++ b/localctlr/LCRuleManager.py @@ -125,9 +125,9 @@ def _find_rules(self, filter={}): x['status']) for x in results] return retval - def list_all_rules(self, switch_id, full_tuple=False): + def list_all_rules(self, full_tuple=False): rules = self.rule_table.find() - self.logger.debug("Retrieving all rules.") + self.logger.debug("---------------cw:Retrieving all rules--------------.") if full_tuple: retval = [(x['cookie'], x['switch_id'], diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index 8e0ad899..90039710 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -209,6 +209,7 @@ def _main_loop(self): self.logger.debug("Received a INSTL message from %s" % hex(id(entry))) self.install_rule_sdxmsg(msg) + self.remove_all_rules_sdxmsg() # If RemoveRule elif type(msg) == SDXMessageRemoveRule: @@ -539,13 +540,34 @@ def install_rule_sdxmsg(self, msg): self.rm.add_rule(cookie, switch_id, rule, RULE_STATUS_INSTALLING) self.switch_connection.send_command(switch_id, rule) - - + self.rm.list_all_rules(True) + self.logger.debug("----------self.rm.list_all_rules complete---------------") #FIXME: This should be moved to somewhere where we have positively #confirmed a rule has been installed. Right now, there is no such #location as the LC/RyuTranslateInteface protocol is primitive. self.rm.set_status(cookie, switch_id, RULE_STATUS_ACTIVE) + def remove_all_rules_sdxmsg(self): + '''CW: this part is not working yet''' + ''' Removes all rules based on cookie sent from the SDX Controller. ''' + rules = self.rm.list_all_rules() + + if rules == []: + self.logger.error("remove_rule_sdxmsg: trying to remove a rule that doesn't exist %s" % cookie) + return + for rule in rules: + print rule + cookie = rule.get_data()['rule'].get_cookie() + switch_id = rule.get_data()['rule'].get_switch_id() + self.rm.set_status(cookie, switch_id, RULE_STATUS_DELETING) + self.switch_connection.remove_rule(switch_id, cookie) + + #FIXME: This should be moved to somewhere where we have positively + #confirmed a rule has been removed. Right now, there is no such + #location as the LC/RyuTranslateInteface protocol is primitive. + self.rm.set_status(cookie, switch_id, RULE_STATUS_REMOVED) + self.rm.rm_rule(cookie, switch_id) + def remove_rule_sdxmsg(self, msg): ''' Removes rules based on cookie sent from the SDX Controller. If we do not have a rule under that cookie, odds are it's for the previous diff --git a/shared/FloodTreeLCRule.py b/shared/FloodTreeLCRule.py index 7b8ee080..4a1061a9 100644 --- a/shared/FloodTreeLCRule.py +++ b/shared/FloodTreeLCRule.py @@ -43,3 +43,9 @@ def __eq__(self, other): def get_ports(self): return self.ports + + def get_switch_id(self): + return self.switch_id + + def get_cookie(self): + return self.cookie From b10b9a19c265c224c75a0e803a336f607e74f7dc Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 23 Mar 2020 16:42:18 -0400 Subject: [PATCH 08/32] Update renci_ben_backup_port.manifest backup port for ncsu --- configuration/renci_testbed/renci_ben_backup_port.manifest | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index ce915c3f..ffddda67 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -306,7 +306,8 @@ "corsaratelimitbridge":"br20", "corsaratelimitports":[21,22], "managementvlan":1411, - "managementvlanports":[1,11] + "managementvlanports":[1,11], + "managementvlanbackupports":[[1, 2]] } } ], From dc278c64118b166d8b58df4987bc3f1149f11f27 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 25 Mar 2020 20:52:53 +0000 Subject: [PATCH 09/32] migrating get_config functions to LocalController.py --- localctlr/LocalController.py | 110 +++++++++++++++++++++++++++-- localctlr/RyuTranslateInterface.py | 2 + shared/FloodTreeLCRule.py | 9 +++ 3 files changed, 115 insertions(+), 6 deletions(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index 90039710..bfacb51b 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -356,6 +356,17 @@ def _get_switch_config_in_db(self, switch_name): val = d['value'] return pickle.loads(str(val)) + def _get_switch_internal_config(self, switch_id): + ''' Gets switch internal config information based on datapath passed in + Pulls information from the DB. + ''' + key = str(switch_id) + d = self.config_table.find_one(key=key) + if d == None: + return None + val = d['value'] + return pickle.loads(str(val)) + def _get_ryu_config_in_db(self): # Returns the ryu configuration dictionary if it exists or None if it # does not. @@ -386,6 +397,35 @@ def _get_SDX_config_in_db(self): val = d['value'] return pickle.loads(str(val)) + def _get_switch_internal_config_count(self): + # Returns a count of internal configs. + d = self.config_table.find() + count = 0 + for entry in d: + print "---------CW--ENTRY:-------------" + print entry + if (entry['key'] == 'lcip' or + entry['key'] == 'manifest_filename' or + entry['key'] == 'ryucxnport'): + continue + count += 1 + return count + + def _add_switch_internal_config_to_db(self, dpid, internal_config): + # Pushes a switch internal_config into the db. + # key: "" + # value: + key = dpid + value = pickle.dumps(internal_config) + if self._get_switch_internal_config(dpid) == None: + self.logger.info("Adding new internal_config for DPID %s" % dpid) + self.config_table.insert({'key': key, 'value': value}) + else: + # Already exists, must update + self.logger.info("updating internal_config for DPID %s" % dpid) + self.config_table.update({'key': key, 'value': value}, + ['key']) + def _get_manifest_filename_in_db(self): # Returns the manifest filename if it exists or None if it does not. key = 'manifest_filename' @@ -395,6 +435,17 @@ def _get_manifest_filename_in_db(self): val = d['value'] return pickle.loads(str(val)) + def _get_config_filename_in_db(self): + # Returns the manifest filename if it exists or None if it does not. + key = 'manifest_filename' + d = self.config_table.find_one(key=key) + print "------------_get_config_filename_in_db-----------" + print d + if d == None: + return None + val = d['value'] + return pickle.loads(str(val)) + def _setup(self, options): self.manifest = options.manifest dbname = options.database @@ -414,7 +465,29 @@ def _setup(self, options): raise Exception("Stored and passed in manifest filenames don't match up %s:%s" % (str(self.manifest), str(self._get_manifest_filename_in_db()))) - + + self.conf_file = None + # If the conf_name is None, try to get the name from the DB. + if self.conf_file == None: + self.conf_file = self._get_config_filename_in_db() + elif (self.conf_file != self._get_config_filename_in_db() and + None != self._get_config_filename_in_db()): + # Make sure it matches! + # FIXME: Should we force everything to be imported if different. + raise Exception("Stored and passed in manifest filenames don't match up %s:%s" % + (str(self.conf_file), + str(self._get_config_filename_in_db()))) + + # Get config file, if it exists + try: + self.logger.info("Opening config file %s" % self.conf_file) + with open(self.conf_file) as data_file: + data = json.load(data_file) + lcdata = data['localcontrollers'][self.name] + except Exception as e: + self.logger.warning("exception when opening config file: %s" % + str(e)) + # Get Manifest, if it exists try: self.logger.info("Opening manifest file %s" % self.manifest) @@ -465,8 +538,27 @@ def _setup(self, options): self.sdxip = options.host self.sdxport = options.sdxport self._add_SDX_config_to_db({'sdxip':self.sdxip, - 'sdxport':self.sdxport}) - + 'sdxport':self.sdxport}) + # OpenFlow/Switch configuration data + config_count = self._get_switch_internal_config_count() + print "------config_count------" + print config_count + if config_count == 0: + # Nothing configured, get configs from config file + for entry in lcdata['switchinfo']: + dpid = str(int(entry['dpid'], 0)) # This is to normalize the DPID. + ic = entry['internalconfig'] + ic['name'] = entry['name'] + self._add_switch_internal_config_to_db(dpid, ic) + + internal_config = self._get_switch_internal_config(204) + if internal_config == None: + print "internal_config == None" + #if 'managementvlan' in internal_config.keys(): + # managementvlan = internal_config['managementvlan'] + #if 'managementvlanports' in internal_config.keys(): + # managementvlanports = internal_config['managementvlanports'] + #print managementvlan def start_sdx_controller_connection(self): # Kick off thread to start connection. @@ -546,6 +638,9 @@ def install_rule_sdxmsg(self, msg): #confirmed a rule has been installed. Right now, there is no such #location as the LC/RyuTranslateInteface protocol is primitive. self.rm.set_status(cookie, switch_id, RULE_STATUS_ACTIVE) + config = self._get_switch_internal_config(switch_id) + self.logger.debug("---------CW: _get_switch_internal_config----------------") + print config def remove_all_rules_sdxmsg(self): '''CW: this part is not working yet''' @@ -556,9 +651,12 @@ def remove_all_rules_sdxmsg(self): self.logger.error("remove_rule_sdxmsg: trying to remove a rule that doesn't exist %s" % cookie) return for rule in rules: - print rule - cookie = rule.get_data()['rule'].get_cookie() - switch_id = rule.get_data()['rule'].get_switch_id() + self.logger.debug("Removing rule:") + self.logger.debug(rule) + self.logger.debug("Type of rule:") + self.logger.debug(type(rule)) + cookie = rule.get_cookie() + switch_id = rule.get_switch_id() self.rm.set_status(cookie, switch_id, RULE_STATUS_DELETING) self.switch_connection.remove_rule(switch_id, cookie) diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index 6678ff4d..03072441 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -356,6 +356,8 @@ def _get_config_filename_in_db(self): # Returns the manifest filename if it exists or None if it does not. key = 'manifest_filename' d = self.config_table.find_one(key=key) + print "cw-RyuTranslate-_get_config_filename_in_db" + print d if d == None: return None val = d['value'] diff --git a/shared/FloodTreeLCRule.py b/shared/FloodTreeLCRule.py index 4a1061a9..c2d2107c 100644 --- a/shared/FloodTreeLCRule.py +++ b/shared/FloodTreeLCRule.py @@ -49,3 +49,12 @@ def get_switch_id(self): def get_cookie(self): return self.cookie + + def set_ports(self, ports): + self.ports = ports + + def set_switch_id(self, switch_id): + self.switch_id = switch_id + + def set_cookie(self, cookie): + self.cookie = cookie From 7466a19b8cb4f38f69be89bdc5d2a120258e1ed6 Mon Sep 17 00:00:00 2001 From: Cong Date: Sun, 29 Mar 2020 14:57:37 +0000 Subject: [PATCH 10/32] pass ManagementLCRecoverRule to RyuTranslateInterface --- localctlr/LocalController.py | 21 +++-- localctlr/RyuTranslateInterface.py | 121 +++++++++++++++++------------ shared/ManagementLCRecoverRule.py | 34 ++++++++ 3 files changed, 120 insertions(+), 56 deletions(-) create mode 100644 shared/ManagementLCRecoverRule.py diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index bfacb51b..e0e188c1 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -21,6 +21,7 @@ from shared.SDXControllerConnectionManager import * from shared.SDXControllerConnectionManagerConnection import * from switch_messages import * +from shared.ManagementLCRecoverRule import * LOCALHOST = "127.0.0.1" DEFAULT_RYU_CXN_PORT = 55767 @@ -165,7 +166,8 @@ def _main_loop(self): except Exception as e: self.logger.error("LocalController: Error in select - %s" % (e)) - + lc_recover = ManagementLCRecoverRule(0, 204) + self.install_rule_sdxmsg(lc_recover) # Loop through readable for entry in readable: # Get Message @@ -209,7 +211,7 @@ def _main_loop(self): self.logger.debug("Received a INSTL message from %s" % hex(id(entry))) self.install_rule_sdxmsg(msg) - self.remove_all_rules_sdxmsg() + #self.remove_all_rules_sdxmsg() # If RemoveRule elif type(msg) == SDXMessageRemoveRule: @@ -622,9 +624,15 @@ def sdx_message_callback(self): # Is this necessary? def install_rule_sdxmsg(self, msg): - switch_id = msg.get_data()['switch_id'] - rule = msg.get_data()['rule'] - cookie = rule.get_cookie() + if type(msg) == ManagementLCRecoverRule: + switch_id = msg.get_switch_id() + rule = msg + cookie = msg.get_cookie() + self.logger.debug("Got ManagementLCRecoverRule-------CW-------") + else: + switch_id = msg.get_data()['switch_id'] + rule = msg.get_data()['rule'] + cookie = rule.get_cookie() self.logger.debug("install_rule_sdxmsg: %d:%s:%s" % (cookie, switch_id, @@ -638,9 +646,6 @@ def install_rule_sdxmsg(self, msg): #confirmed a rule has been installed. Right now, there is no such #location as the LC/RyuTranslateInteface protocol is primitive. self.rm.set_status(cookie, switch_id, RULE_STATUS_ACTIVE) - config = self._get_switch_internal_config(switch_id) - self.logger.debug("---------CW: _get_switch_internal_config----------------") - print config def remove_all_rules_sdxmsg(self): '''CW: this part is not working yet''' diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index 03072441..a3a9dc82 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -37,6 +37,7 @@ from shared.L2MultipointLearnedDestinationLCRule import * from shared.FloodTreeLCRule import * from shared.ManagementVLANLCRule import * +from shared.ManagementLCRecoverRule import * LOCALHOST = "127.0.0.1" @@ -489,13 +490,13 @@ def switch_features_handler(self, ev): self._new_switch_bootstrapping(ev) # Handles port status change event - @set_ev_cls(ofp_event.EventOFPPortStatus, CONFIG_DISPATCHER) - def port_status_handler(self, ev): - self.logger.warning("Port status changed: connection from: " + str(ev.msg.datapath.id) + " for " + str(self)) - self.datapaths[ev.msg.datapath.id] = ev.msg.datapath + #@set_ev_cls(ofp_event.EventOFPPortStatus, CONFIG_DISPATCHER) + #def port_status_handler(self, ev): + # self.logger.warning("Port status changed: connection from: " + str(ev.msg.datapath.id) + " for " + str(self)) + # self.datapaths[ev.msg.datapath.id] = ev.msg.datapath # Call backup port recovery for switch functions - self._backup_port_recover(ev) + # self._backup_port_recover(ev) # From the Ryu mailing list: https://sourceforge.net/p/ryu/mailman/message/33584125/ @set_ev_cls(ofp_event.EventOFPErrorMsg, @@ -529,6 +530,8 @@ def _new_switch_bootstrapping(self, ev): switch_id = 0 # This is unimportant: # it's never used in the translation datapath = ev.msg.datapath + #print "----------_new_switch_bootstrapping----------CW----------dpid:" + #print datapath self.remove_all_flows(datapath) @@ -606,14 +609,12 @@ def _new_switch_bootstrapping(self, ev): for rule in results: self.add_flow(datapath, rule) - def _backup_port_recover_remove_all(self, ev): + def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): '''Remove existing default port and use backup port''' + self.logger.debug("got ManagementLCRecoverRule-----CW---------") self.logger.debug("Checking if backup port is available") switch_id = 0 # This is unimportant: # it's never used in the translation - datapath = ev.msg.datapath - - self.remove_all_flows(datapath) of_cookie = self._get_new_OF_cookie(-1) # FIXME: magic number results = [] @@ -633,47 +634,44 @@ def _backup_port_recover_remove_all(self, ev): managementvlanbackupports = internal_config['managementvlanbackupports'] else: self.logger.debug("No backup port provided") - return - - for table in ALL_TABLES_EXCEPT_LAST: - matches = [] # FIXME: what's the equivalent of match(*)? - actions = [Continue()] - priority = PRIORITY_DEFAULT - marule = MatchActionLCRule(switch_id, matches, actions) - results += self._translate_MatchActionLCRule(datapath, - table, - of_cookie, - marule, - priority) + #return + + #for table in ALL_TABLES_EXCEPT_LAST: + # matches = [] # FIXME: what's the equivalent of match(*)? + # actions = [Continue()] + # priority = PRIORITY_DEFAULT + # marule = MatchActionLCRule(switch_id, matches, actions) + # results += self._translate_MatchActionLCRule(datapath, + # table, + # of_cookie, + # marule, + # priority) # For last table # - Create a default drop rule (if necessary needed). Priority 0 - for i in (managementvlanports): - self.logger.debug("Using default management ports.") - for j in (managementvlanbackupports): - if i == j[0] and len(j) > 1: - i = j[1] - matches = [IN_PORT(i)] - actions = [Drop()] - priority = PRIORITY_DEFAULT_PLUS_ONE - table = LASTTABLE - marule = MatchActionLCRule(switch_id, matches, actions) - results += self._translate_MatchActionLCRule(datapath, - table, - of_cookie, - marule, - priority) + #for i in (managementvlanports): + # self.logger.debug("Using default management ports.") + # matches = [IN_PORT(i)] + # actions = [Drop()] + # priority = PRIORITY_DEFAULT_PLUS_ONE + # table = LASTTABLE + # marule = MatchActionLCRule(switch_id, matches, actions) + # results += self._translate_MatchActionLCRule(datapath, + # table, + # of_cookie, + # marule, + # priority) # Catch-all for those not in the same port - matches = [] - actions = [Drop()] - priority = PRIORITY_DEFAULT - table = LASTTABLE - marule = MatchActionLCRule(switch_id, matches, actions) - results += self._translate_MatchActionLCRule(datapath, - table, - of_cookie, - marule, - priority) + #matches = [] + #actions = [Drop()] + #priority = PRIORITY_DEFAULT + #table = LASTTABLE + #marule = MatchActionLCRule(switch_id, matches, actions) + #results += self._translate_MatchActionLCRule(datapath, + # table, + # of_cookie, + # marule, + # priority) # In-band Communication # If the management VLAN needs to be setup, set it up. @@ -694,13 +692,37 @@ def _backup_port_recover_remove_all(self, ev): of_cookie, mvrule) + print "---------CW-------REMOVING default flows" # Install default rules for rule in results: - self.add_flow(datapath, rule) + self.remove_flow(datapath, rule) + + # Add backup management vlan ports + results = [] + if 'managementvlan' in internal_config.keys(): + managementvlan = internal_config['managementvlan'] + managementvlanports = internal_config['managementvlanbackupports'] + untaggedmanagementvlanports = [] + if 'untaggedmanagementvlanports' in internal_config.keys(): + untaggedmanagementvlanports = internal_config['untaggedmanagementvlanports'] + table = L2TUNNELTABLE + mvrule = ManagementVLANLCRule(switch_id, + managementvlan, + managementvlanports, + untaggedmanagementvlanports) + results += self._translate_ManagementVLANLCRule(datapath, + table, + of_cookie, + mvrule) + + print "---------CW-------ADDING default flows" + # Install default rules + for rule in results: + self.add_flow(datapath, rule) - def _backup_port_recover(self, ev): + def _toberemoved_backup_port_recover(self, ev): '''Remove existing default port and use backup port''' self.logger.debug("Checking if backup port is available") rule_results = [] @@ -1752,6 +1774,9 @@ def install_rule(self, datapath, sdx_rule): of_cookie, sdx_rule) + elif isinstance(sdx_rule, ManagementLCRecoverRule): + self._backup_port_recover(datapath, of_cookie, sdx_rule) + if switch_rules == None or switch_table == None: self.logger.error( "switch_rules or switch_table is None for msg: %s\n switch_rules - %s\n switch_table - %s" % diff --git a/shared/ManagementLCRecoverRule.py b/shared/ManagementLCRecoverRule.py new file mode 100644 index 00000000..b6a516a8 --- /dev/null +++ b/shared/ManagementLCRecoverRule.py @@ -0,0 +1,34 @@ +# Copyright 2018 - Sean Donovan +# AtlanticWave/SDX Project + +from LCRule import * + +class ManagementLCRecoverRule(LCRule): + ''' This structure is used to pass the ports that belong to a spanning tree + to the Local Controller for handling broadcast flooding without cycles. + Created by FloodTreePolicy. ''' + + def __init__(self, cookie, switch_id): + ''' Field descritpions: + switch_id - Which switch is involved + ports - List of ports that are part of the spanning tree needed + for broadcast flooding. + ''' + super(ManagementLCRecoverRule, self).__init__(self, switch_id) + self.cookie = cookie + self.switch_id = switch_id + + def __str__(self): + retstr = ("ManagementLCRecoverRule: switch %s" % + (self.switch_id)) + return retstr + + def __eq__(self, other): + return (type(self) == type(other) and + self.get_switch_id() == other.get_switch_id()) + + def get_switch_id(self): + return self.switch_id + + def get_cookie(self): + return self.cookie From 0625d7920b20bfbfa02c3ea926e2ef443542cf63 Mon Sep 17 00:00:00 2001 From: Cong Date: Sun, 29 Mar 2020 16:33:29 +0000 Subject: [PATCH 11/32] adding recovery functions --- localctlr/LocalController.py | 23 +++++++++++++---------- localctlr/RyuTranslateInterface.py | 8 +++++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index e0e188c1..19f945ac 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -152,6 +152,8 @@ def _main_loop(self): if (self.sdx_connection == None and self.start_cxn_thread == None): self.logger.info("Restarting SDX Connection") + lc_recover = ManagementLCRecoverRule(0, 204) + self.install_rule_sdxmsg(lc_recover) self.start_sdx_controller_connection() #Restart! if len(rlist) == 0: @@ -166,8 +168,8 @@ def _main_loop(self): except Exception as e: self.logger.error("LocalController: Error in select - %s" % (e)) - lc_recover = ManagementLCRecoverRule(0, 204) - self.install_rule_sdxmsg(lc_recover) + #lc_recover = ManagementLCRecoverRule(0, 204) + #self.install_rule_sdxmsg(lc_recover) # Loop through readable for entry in readable: # Get Message @@ -253,13 +255,14 @@ def _main_loop(self): self.sdx_connection.close() self.sdx_connection = None self.cxn_q.put((DEL_CXN, cxn)) - if num_of_retry <= 5: + #if num_of_retry <= 5: # Restart new connection - self.start_sdx_controller_connection() - sleep(5) - - self.all_rules = self.rm.list_all_rules - num_of_retry = 0 + # self.start_sdx_controller_connection() + # sleep(5) + lc_recover = ManagementLCRecoverRule(0, 204) + self.install_rule_sdxmsg(recover) + self.start_sdx_controller_connection() + #num_of_retry = 0 @@ -640,8 +643,8 @@ def install_rule_sdxmsg(self, msg): self.rm.add_rule(cookie, switch_id, rule, RULE_STATUS_INSTALLING) self.switch_connection.send_command(switch_id, rule) - self.rm.list_all_rules(True) - self.logger.debug("----------self.rm.list_all_rules complete---------------") + #self.rm.list_all_rules(True) + #self.logger.debug("----------self.rm.list_all_rules complete---------------") #FIXME: This should be moved to somewhere where we have positively #confirmed a rule has been installed. Right now, there is no such #location as the LC/RyuTranslateInteface protocol is primitive. diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index a3a9dc82..e21f94c7 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -1776,11 +1776,13 @@ def install_rule(self, datapath, sdx_rule): elif isinstance(sdx_rule, ManagementLCRecoverRule): self._backup_port_recover(datapath, of_cookie, sdx_rule) + return if switch_rules == None or switch_table == None: - self.logger.error( - "switch_rules or switch_table is None for msg: %s\n switch_rules - %s\n switch_table - %s" % - sdx_rule, switch_rules, switch_table) + if not isinstance(sdx_rule, ManagementLCRecoverRule): + self.logger.error( + "switch_rules or switch_table is None for msg: %s\n switch_rules - %s\n switch_table - %s" % + sdx_rule, switch_rules, switch_table) # FIXME: This shouldn't happen... pass From 40ef79dd4e3be0ebf0a18d81242bac50d39c4237 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 30 Mar 2020 22:36:30 +0000 Subject: [PATCH 12/32] add debug message --- sdxctlr/SDXController.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdxctlr/SDXController.py b/sdxctlr/SDXController.py index 78113d0b..049bc091 100644 --- a/sdxctlr/SDXController.py +++ b/sdxctlr/SDXController.py @@ -200,6 +200,8 @@ def _handle_new_connection(self, cxn): topo = self.tm.get_topology() self.logger.warning("New connection - name %s details %s" % (name, cxn)) for switch in topo.node[name]['switches']: + print "-----------CW--------SDXController: for switch in topo.node[name]['switches']:------------" + print switch json_rule = {"EdgePort":{"switch":switch}} epp = EdgePortPolicy(AUTOGENERATED_USERNAME, json_rule) self.rm.add_rule(epp) @@ -213,7 +215,8 @@ def _handle_connection_loss(self, cxn): # Get LC name name = cxn.get_name() - + print "------------CW-----------SDXController: _handle_connection_loss(self, cxn):name = cxn.get_name()-------" + print name # Delete connections associations self.sdx_cm.dissociate_name_with_cxn(name) # Get all EdgePort policies From d42e0a9b717666baaae7740b9e68bb6d30079a8c Mon Sep 17 00:00:00 2001 From: root Date: Thu, 2 Apr 2020 21:19:18 +0000 Subject: [PATCH 13/32] add ManagementSDXRecoverPolicy.py --- shared/EdgePortPolicy.py | 4 + shared/ManagementSDXRecoverPolicy.py | 117 +++++++++++++++++++++++ shared/ManagementSDXRecoverRule.py | 61 ++++++++++++ shared/SDXControllerConnectionManager.py | 4 + 4 files changed, 186 insertions(+) create mode 100644 shared/ManagementSDXRecoverPolicy.py create mode 100644 shared/ManagementSDXRecoverRule.py diff --git a/shared/EdgePortPolicy.py b/shared/EdgePortPolicy.py index 1a9290d3..89936e08 100644 --- a/shared/EdgePortPolicy.py +++ b/shared/EdgePortPolicy.py @@ -68,7 +68,11 @@ def breakdown_rule(self, tm, ai): topology = tm.get_topology() authorization_func = ai.is_authorized switch_id = topology.node[self.switch]['dpid'] + print "---------CW-------EdgePortPolicy------switch_id = topology.node[self.switch]['dpid']------------" + print switch_id shortname = topology.node[self.switch]['locationshortname'] + print "---------CW-------EdgePortPolicy------shortname = topology.node[self.switch]['locationshortname']------------" + print shortname bd = UserPolicyBreakdown(shortname, []) diff --git a/shared/ManagementSDXRecoverPolicy.py b/shared/ManagementSDXRecoverPolicy.py new file mode 100644 index 00000000..6813542d --- /dev/null +++ b/shared/ManagementSDXRecoverPolicy.py @@ -0,0 +1,117 @@ +# Copyright 2017 - Sean Donovan +# AtlanticWave/SDX Project + + +from UserPolicy import * +from datetime import datetime +from shared.constants import * +from EdgePortLCRule import * +from ManagementSDXRecoverRule import * +import networkx as nx + +class ManagementSDXRecoverPolicy(UserPolicy): + ''' This policy is used during initialization of the LocalController to let + it know which ports of it are edge ports. This is necessary for proper + learning of new paths to occur without redundant "new destination" + messages coming in from switch-to-switch ports. + + It requires the following information at intialization: + - Switch + + Example Json: + {"EdgePort":{ + "switch":"mia-switch"}} + + The vast majority of the work is handled by the breakdown_rule() function + which uses the topology to determine which ports are actually the edge + ports. + ''' + + def __init__(self, username, json_rule): + self.switch = None + + super(ManagementSDXRecoverPolicy, self).__init__(username, + json_rule) + + # Anything specific here? + pass + + def __str__(self): + return "%s(%s)" % (self.get_policy_name(), self.switch) + + @classmethod + def check_syntax(cls, json_rule): + try: + # Make sure the times are the right format + # https://stackoverflow.com/questions/455580/json-datetime-between-python-and-javascript + # 'switch' is the LocalController NAME! + switch = json_rule[cls.get_policy_name()]['switch'] + + except Exception as e: + import os + exc_type, exc_obj, exc_tb = sys.exc_info() + filename = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + lineno = exc_tb.tb_lineno + print "%s: Exception %s at %s:%d" % (self.get_policy_name(), + str(e), filename,lineno) + raise + + def breakdown_rule(self, tm, ai): + ''' There are two stages to breaking down these rules: + - determine edge ports for the local switch + - create EdgePortLCRules for each edge port + To determine which ports are edge port, we look at each port and see + what type the neighbor is. If they are a "switch" type, then that's + an internal port, otherwise, it's an edge port. + ''' + + self.breakdown = [] + topology = tm.get_topology() + authorization_func = ai.is_authorized + switch_id = topology.node[self.switch]['dpid'] + shortname = topology.node[self.switch]['locationshortname'] + + bd = UserPolicyBreakdown(shortname, []) + + msr = ManagementSDXRecoverRule(switch_id) + bd.add_to_list_of_rules(msr) + + self.breakdown.append(bd) + return self.breakdown + + + def check_validity(self, tm, ai): + #FIXME: This is going to be skipped for now, as we need to figure out what's authorized and what's not. + return True + + def _parse_json(self, json_rule): + jsonstring = self.ruletype + if type(json_rule) is not dict: + raise UserPolicyTypeError("json_rule is not a dictionary:\n %s" % json_rule) + if jsonstring not in json_rule.keys(): + raise UserPolicyValueError("%s value not in entry:\n %s" % ('rules', json_rule)) + + self.switch = str(json_rule[jsonstring]['switch']) + + #FIXME: Really need some type verifications here. + + + def pre_add_callback(self, tm, ai): + ''' This is called before a rule is added to the database. For instance, + if certain resources need to be locked down or rules authorized, + this can do it. May not need to be implemented. ''' + pass + + def pre_remove_callback(self, tm, ai): + ''' This is called before a rule is removed from the database. For + instance, if certain resources need to be released, this can do it. + May not need to be implemented. ''' + + pass + + + + + + + diff --git a/shared/ManagementSDXRecoverRule.py b/shared/ManagementSDXRecoverRule.py new file mode 100644 index 00000000..3195fdde --- /dev/null +++ b/shared/ManagementSDXRecoverRule.py @@ -0,0 +1,61 @@ +# Copyright 2018 - Sean Donovan +# AtlanticWave/SDX Project + +from LCRule import * + +# Some noteson the current implementation: +# - This version is a first pass at implementing the Management VLAN. There are +# a number of features that are skipped or implemented differently. +# - This version, like other parts of the controller, is not resillient in +# the face of failures. That is, a link failure may take down the +# management VLAN if that link was integral to the Management VLAN spanning +# tree. +# - Spanning Tree is necessary to avoid cycles. Cycles are *bad*. +# - The Spanning Tree that is used is manually configured in the manifest. +# - All messages are flooded +# - Future changes that are necessary: +# - Implement localized Spanning Tree Protocol for Management VLAN +# - This need not be 802.1 STP, but we need to be able to distributedly +# build a spanning tree. +# - When Spanning Tree is rebuilt due to failure, Updates to the rules are +# needed +# - Flooding should be updated to be learned. This probably will need to be +# internal to the LC. +# - This would require certain updates to the RyuTranslateInterface to handle +# all this new functionality. +# - Learning +# - Connect/disconnect + +# Copyright 2017 - Sean Donovan +# AtlanticWave/SDX Project + +from LCRule import * + +class ManagementSDXRecoverRule(LCRule): + ''' This structure is used to pass the ports that belong to a spanning tree + to the Local Controller for handling broadcast flooding without cycles. + Created by FloodTreePolicy. ''' + + def __init__(self, switch_id): + ''' Field descritpions: + switch_id - Which switch is involved + ports - List of ports that are part of the spanning tree needed + for broadcast flooding. + ''' + super(ManagementSDXRecoverRule, self).__init__(self, switch_id) + + + def __str__(self): + retstr = ("ManagementSDXRecoverRule: switch %s" % + (self.switch_id)) + return retstr + + def __eq__(self, other): + return (type(self) == type(other) and + self.get_switch_id() == other.get_switch_id()) + + def get_switch_id(self): + return self.switch_id + + def get_cookie(self): + return self.cookie diff --git a/shared/SDXControllerConnectionManager.py b/shared/SDXControllerConnectionManager.py index c8f428a7..9dcb7908 100644 --- a/shared/SDXControllerConnectionManager.py +++ b/shared/SDXControllerConnectionManager.py @@ -63,6 +63,10 @@ def send_breakdown_rule_add(self, bd): for rule in bd.get_list_of_rules(): switch_id = rule.get_switch_id() msg = SDXMessageInstallRule(rule, switch_id) + print "---------CW send_breakdown_rule_add---------msg------------" + print msg + print "---------CW send_breakdown_rule_add---------rule-----------" + print rule lc_cxn.send_protocol(msg) except SDXControllerConnectionManagerNotConnectedError as e: From 9e88d67bf25f00400e087f3bc1b53edca7fe2596 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 3 Apr 2020 21:55:56 +0000 Subject: [PATCH 14/32] SDX send recover policy to LC --- sdxctlr/RuleManager.py | 4 ++++ sdxctlr/SDXController.py | 8 +++++++- shared/ManagementSDXRecoverPolicy.py | 12 +++++++++--- shared/ManagementSDXRecoverRule.py | 3 ++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/sdxctlr/RuleManager.py b/sdxctlr/RuleManager.py index 46bf6517..1e4171ed 100644 --- a/sdxctlr/RuleManager.py +++ b/sdxctlr/RuleManager.py @@ -558,8 +558,12 @@ def _install_breakdown(self, breakdown): try: for bd in breakdown: self.logger.debug("Sending install breakdown: %s" % bd) + print "before bd.get_list_of_rules()" for rule in bd.get_list_of_rules(): + print "rule:" + print rule self.logger.debug(" %s" % str(rule)) + print "after bd.get_list_of_rules()" self.send_user_add_rule(bd) except Exception as e: raise diff --git a/sdxctlr/SDXController.py b/sdxctlr/SDXController.py index 049bc091..0a86d78e 100644 --- a/sdxctlr/SDXController.py +++ b/sdxctlr/SDXController.py @@ -32,7 +32,7 @@ from shared.LearnedDestinationPolicy import * from shared.FloodTreePolicy import * from shared.SDXPolicy import SDXEgressPolicy, SDXIngressPolicy - +from shared.ManagementSDXRecoverPolicy import * class SDXControllerError(Exception): @@ -105,6 +105,7 @@ def __init__(self, runloop=True, options=None): self.rr.add_ruletype(FloodTreePolicy) self.rr.add_ruletype(SDXEgressPolicy) self.rr.add_ruletype(SDXIngressPolicy) + self.rr.add_ruletype(ManagementSDXRecoverPolicy) # Start these modules last! if self.run_topo: @@ -204,7 +205,12 @@ def _handle_new_connection(self, cxn): print switch json_rule = {"EdgePort":{"switch":switch}} epp = EdgePortPolicy(AUTOGENERATED_USERNAME, json_rule) + json_rule = {"ManagementSDXRecover":{"switch":switch}} + msr = ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule) + print "-----------CW--------SDXController:ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule)-------" + print json_rule self.rm.add_rule(epp) + self.rm.add_rule(msr) def _handle_connection_loss(self, cxn): #FIXME: Send this to the LocalControllerManager diff --git a/shared/ManagementSDXRecoverPolicy.py b/shared/ManagementSDXRecoverPolicy.py index 6813542d..20acdb6c 100644 --- a/shared/ManagementSDXRecoverPolicy.py +++ b/shared/ManagementSDXRecoverPolicy.py @@ -64,17 +64,18 @@ def breakdown_rule(self, tm, ai): what type the neighbor is. If they are a "switch" type, then that's an internal port, otherwise, it's an edge port. ''' - + print "-----CW-breakdown_rule(self, tm, ai):-------" self.breakdown = [] topology = tm.get_topology() authorization_func = ai.is_authorized switch_id = topology.node[self.switch]['dpid'] shortname = topology.node[self.switch]['locationshortname'] - bd = UserPolicyBreakdown(shortname, []) - msr = ManagementSDXRecoverRule(switch_id) bd.add_to_list_of_rules(msr) + + for i in bd.get_list_of_rules(): + print self.breakdown.append(bd) return self.breakdown @@ -86,6 +87,11 @@ def check_validity(self, tm, ai): def _parse_json(self, json_rule): jsonstring = self.ruletype + + #print "rule type:" + str(jsonstring) + #print "json_rule.keys():" + #for i in json_rule.keys(): + # print i if type(json_rule) is not dict: raise UserPolicyTypeError("json_rule is not a dictionary:\n %s" % json_rule) if jsonstring not in json_rule.keys(): diff --git a/shared/ManagementSDXRecoverRule.py b/shared/ManagementSDXRecoverRule.py index 3195fdde..502fc4b5 100644 --- a/shared/ManagementSDXRecoverRule.py +++ b/shared/ManagementSDXRecoverRule.py @@ -42,7 +42,8 @@ def __init__(self, switch_id): ports - List of ports that are part of the spanning tree needed for broadcast flooding. ''' - super(ManagementSDXRecoverRule, self).__init__(self, switch_id) + self.switch_id = switch_id + super(ManagementSDXRecoverRule, self).__init__(switch_id) def __str__(self): From 55efd350075340bcdf825c4eb77718fbb8f652ee Mon Sep 17 00:00:00 2001 From: root Date: Sat, 4 Apr 2020 13:55:46 +0000 Subject: [PATCH 15/32] LC recover port from SDX msg --- .../renci_ben_backup_port.manifest | 4 +- localctlr/RyuTranslateInterface.py | 117 ++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index ffddda67..c691c633 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -253,7 +253,7 @@ "corsaratelimitports":[21,22], "managementvlan":1411, "managementvlanports":[1,11], - "managementvlanbackupports":[[1, 2]] + "managementvlanbackupports":[1, 2, 11] } } ], @@ -307,7 +307,7 @@ "corsaratelimitports":[21,22], "managementvlan":1411, "managementvlanports":[1,11], - "managementvlanbackupports":[[1, 2]] + "managementvlanbackupports":[1, 2, 11] } } ], diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index e21f94c7..7d9dbfca 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -38,6 +38,7 @@ from shared.FloodTreeLCRule import * from shared.ManagementVLANLCRule import * from shared.ManagementLCRecoverRule import * +from shared.ManagementSDXRecoverRule import * LOCALHOST = "127.0.0.1" @@ -721,6 +722,117 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): for rule in results: self.add_flow(datapath, rule) + def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule): + '''Remove existing default port and use backup port''' + self.logger.debug("got ManagementLCRecoverRule-----CW---------") + self.logger.debug("Checking if backup port is available") + switch_id = 0 # This is unimportant: + # it's never used in the translation + + of_cookie = self._get_new_OF_cookie(-1) # FIXME: magic number + results = [] + + # In-band Communication + # Extract management VLAN and ports from the manifest + internal_config = self._get_switch_internal_config(datapath.id) + if internal_config == None: + raise ValueError("DPID %s does not have internal_config" % + datapath.id) + if 'managementvlan' in internal_config.keys(): + managementvlan = internal_config['managementvlan'] + if 'managementvlanports' in internal_config.keys(): + managementvlanports = internal_config['managementvlanports'] + + if 'sdxmanagementvlanbackupports' in internal_config.keys(): + managementvlanbackupports = internal_config['sdxmanagementvlanbackupports'] + else: + self.logger.debug("No backup port provided") + #return + + #for table in ALL_TABLES_EXCEPT_LAST: + # matches = [] # FIXME: what's the equivalent of match(*)? + # actions = [Continue()] + # priority = PRIORITY_DEFAULT + # marule = MatchActionLCRule(switch_id, matches, actions) + # results += self._translate_MatchActionLCRule(datapath, + # table, + # of_cookie, + # marule, + # priority) + + # For last table + # - Create a default drop rule (if necessary needed). Priority 0 + #for i in (managementvlanports): + # self.logger.debug("Using default management ports.") + # matches = [IN_PORT(i)] + # actions = [Drop()] + # priority = PRIORITY_DEFAULT_PLUS_ONE + # table = LASTTABLE + # marule = MatchActionLCRule(switch_id, matches, actions) + # results += self._translate_MatchActionLCRule(datapath, + # table, + # of_cookie, + # marule, + # priority) + # Catch-all for those not in the same port + #matches = [] + #actions = [Drop()] + #priority = PRIORITY_DEFAULT + #table = LASTTABLE + #marule = MatchActionLCRule(switch_id, matches, actions) + #results += self._translate_MatchActionLCRule(datapath, + # table, + # of_cookie, + # marule, + # priority) + + # In-band Communication + # If the management VLAN needs to be setup, set it up. + if 'managementvlan' in internal_config.keys(): + managementvlan = internal_config['managementvlan'] + managementvlanports = internal_config['managementvlanports'] + untaggedmanagementvlanports = [] + if 'untaggedmanagementvlanports' in internal_config.keys(): + untaggedmanagementvlanports = internal_config['untaggedmanagementvlanports'] + + table = L2TUNNELTABLE + mvrule = ManagementVLANLCRule(switch_id, + managementvlan, + managementvlanports, + untaggedmanagementvlanports) + results += self._translate_ManagementVLANLCRule(datapath, + table, + of_cookie, + mvrule) + + print "---------CW-------REMOVING default flows" + # Install default rules + for rule in results: + self.remove_flow(datapath, rule) + + # Add backup management vlan ports + results = [] + if 'managementvlan' in internal_config.keys(): + managementvlan = internal_config['managementvlan'] + managementvlanports = internal_config['managementvlanbackupports'] + untaggedmanagementvlanports = [] + if 'untaggedmanagementvlanports' in internal_config.keys(): + untaggedmanagementvlanports = internal_config['untaggedmanagementvlanports'] + + table = L2TUNNELTABLE + mvrule = ManagementVLANLCRule(switch_id, + managementvlan, + managementvlanports, + untaggedmanagementvlanports) + results += self._translate_ManagementVLANLCRule(datapath, + table, + of_cookie, + mvrule) + + print "---------CW-------ADDING default flows" + # Install default rules + for rule in results: + self.add_flow(datapath, rule) def _toberemoved_backup_port_recover(self, ev): '''Remove existing default port and use backup port''' @@ -1778,6 +1890,11 @@ def install_rule(self, datapath, sdx_rule): self._backup_port_recover(datapath, of_cookie, sdx_rule) return + elif isinstance(sdx_rule, ManagementSDXRecoverRule): + print "Got ManagementSDXRecoverRule---------CW----------" + self._backup_port_recover_from_sdx_msg(datapath, of_cookie, sdx_rule) + return + if switch_rules == None or switch_table == None: if not isinstance(sdx_rule, ManagementLCRecoverRule): self.logger.error( From 078b6be2b7330c47ee8acf2777719f3b40f155ca Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 4 Apr 2020 09:57:52 -0400 Subject: [PATCH 16/32] remove unused function --- localctlr/RyuTranslateInterface.py | 67 ------------------------------ 1 file changed, 67 deletions(-) diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index 7d9dbfca..a890b829 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -834,73 +834,6 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule for rule in results: self.add_flow(datapath, rule) - def _toberemoved_backup_port_recover(self, ev): - '''Remove existing default port and use backup port''' - self.logger.debug("Checking if backup port is available") - rule_results = [] - switch_id = 0 - datapath = ev.msg.datapath - of_cookie = self._get_new_OF_cookie(-1) - # Extract management VLAN and ports from the manifest - internal_config = self._get_switch_internal_config(datapath.id) - - if internal_config == None: - raise ValueError("DPID %s does not have internal_config" % - datapath.id) - - #if 'managementvlanports' in internal_config.keys(): - # managementvlanports = internal_config['managementvlanports'] - - if 'managementvlanbackupports' in internal_config.keys(): - managementvlanbackupports = internal_config['managementvlanbackupports'] - else: - self.logger.debug("No backup port provided") - return - - for i in (managementvlanbackupports): - # If port has backup port, use first one as default port - if isinstance(i, (list)): - if len(i) > 1: - i = i[0] - matches = [IN_PORT(i)] - actions = [Drop()] - priority = PRIORITY_DEFAULT_PLUS_ONE - table = LASTTABLE - marule = MatchActionLCRule(switch_id, matches, actions) - rule_results += self._translate_MatchActionLCRule(datapath, - table, - of_cookie, - marule, - priority) - - # Remove default rules - self.logger.debug("Removing default management VLAN port") - for rule in rule_results: - self.remove_flow(datapath, rule) - - rule_results = [] - - for i in (managementvlanbackupports): - # If port has backup port, use first one as default port - if isinstance(i, (list)): - if len(i) > 1: - i = i[1] - matches = [IN_PORT(i)] - actions = [Drop()] - priority = PRIORITY_DEFAULT_PLUS_ONE - table = LASTTABLE - marule = MatchActionLCRule(switch_id, matches, actions) - rule_results += self._translate_MatchActionLCRule(datapath, - table, - of_cookie, - marule, - priority) - - # Install backup rules - self.logger.debug("Adding new management VLAN port") - for rule in rule_results: - self.add_flow(datapath, rule) - def _translate_MatchActionLCRule(self, datapath, table, of_cookie, marule, priority=100): ''' This translates MatchActionLCRules. There is only one rule generated From 8aeba32cb9eb4c45ce19245371150ab222fbbe7b Mon Sep 17 00:00:00 2001 From: root Date: Sat, 4 Apr 2020 15:35:27 +0000 Subject: [PATCH 17/32] sdx send recover request to lc --- sdxctlr/SDXController.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sdxctlr/SDXController.py b/sdxctlr/SDXController.py index 0a86d78e..06861bf8 100644 --- a/sdxctlr/SDXController.py +++ b/sdxctlr/SDXController.py @@ -205,12 +205,12 @@ def _handle_new_connection(self, cxn): print switch json_rule = {"EdgePort":{"switch":switch}} epp = EdgePortPolicy(AUTOGENERATED_USERNAME, json_rule) - json_rule = {"ManagementSDXRecover":{"switch":switch}} - msr = ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule) - print "-----------CW--------SDXController:ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule)-------" - print json_rule + #json_rule = {"ManagementSDXRecover":{"switch":switch}} + #msr = ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule) + #print "-----------CW--------SDXController:ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule)-------" + #print json_rule self.rm.add_rule(epp) - self.rm.add_rule(msr) + #self.rm.add_rule(msr) def _handle_connection_loss(self, cxn): #FIXME: Send this to the LocalControllerManager @@ -230,6 +230,9 @@ def _handle_connection_loss(self, cxn): # for each switch owned by a particular LC, delete that particular rule topo = self.tm.get_topology() + print "------------CW-----------internalconfig----------" + backuplc = topo.node[name]['internalconfig']['backuplc'] + for switch in topo.node[name]['switches']: json_rule = {"EdgePort":{"switch":switch}} for p in all_edgeport_policies: @@ -237,7 +240,9 @@ def _handle_connection_loss(self, cxn): if json_rule == json_ver: self.rm.remove_rule(rule_hash, user) - + json_rule = {"ManagementSDXRecover":{"switch":backuplc}} + msr = ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule) + self.rm.add_rule(msr) pass def start_main_loop(self): From 68d391c6b125808641a2d6869e102d82afee5496 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Apr 2020 02:48:56 +0000 Subject: [PATCH 18/32] manifest with backup lc --- .../renci_testbed/renci_ben_backup_port.manifest | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index c691c633..df3a8feb 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -59,7 +59,8 @@ "lcip": "10.14.11.1", "internalconfig": { "ryucxninternalport": 55781, - "openflowport": 6681 + "openflowport": 6681, + "backuplcswitch": "dukes1" }, "switchinfo": [ { @@ -158,7 +159,8 @@ "lcip": "10.14.11.2", "internalconfig": { "ryucxninternalport": 55782, - "openflowport": 6682 + "openflowport": 6682, + "backuplcswitch": "rencis1" }, "switchinfo": [ { @@ -216,7 +218,8 @@ "lcip": "10.14.11.3", "internalconfig": { "ryucxninternalport": 55783, - "openflowport": 6683 + "openflowport": 6683, + "backuplcswitch": "ncsus1" }, "switchinfo": [ { @@ -254,6 +257,7 @@ "managementvlan":1411, "managementvlanports":[1,11], "managementvlanbackupports":[1, 2, 11] + "sdxmanagementvlanbackupports":[1, 2, 11] } } ], @@ -270,7 +274,8 @@ "lcip": "10.14.11.4", "internalconfig": { "ryucxninternalport": 55784, - "openflowport": 6684 + "openflowport": 6684, + "backuplc": "uncs1" }, "switchinfo": [ { From f35705e5aa926446c3bf03758b165362ee84b621 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Apr 2020 02:51:57 +0000 Subject: [PATCH 19/32] manufest update --- configuration/renci_testbed/renci_ben_backup_port.manifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index df3a8feb..85e29ca7 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -256,7 +256,7 @@ "corsaratelimitports":[21,22], "managementvlan":1411, "managementvlanports":[1,11], - "managementvlanbackupports":[1, 2, 11] + "managementvlanbackupports":[1, 2, 11], "sdxmanagementvlanbackupports":[1, 2, 11] } } From 5a301241c4537ab0d1b0923d84107f4ec78cdabc Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Apr 2020 02:56:08 +0000 Subject: [PATCH 20/32] minor change on manifest --- configuration/renci_testbed/renci_ben_backup_port.manifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/renci_testbed/renci_ben_backup_port.manifest b/configuration/renci_testbed/renci_ben_backup_port.manifest index 85e29ca7..f34e5f5c 100644 --- a/configuration/renci_testbed/renci_ben_backup_port.manifest +++ b/configuration/renci_testbed/renci_ben_backup_port.manifest @@ -312,7 +312,7 @@ "corsaratelimitports":[21,22], "managementvlan":1411, "managementvlanports":[1,11], - "managementvlanbackupports":[1, 2, 11] + "managementvlanbackupports":[2, 11] } } ], From 80174bbdaad45b1526067cc4126e2f113d8a4cdd Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Apr 2020 02:58:29 +0000 Subject: [PATCH 21/32] add debug messages --- localctlr/RyuTranslateInterface.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index a890b829..4762b96c 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -693,7 +693,7 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): of_cookie, mvrule) - print "---------CW-------REMOVING default flows" + self.logger.debug("---------CW-------REMOVING default flows") # Install default rules for rule in results: self.remove_flow(datapath, rule) @@ -702,7 +702,7 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): results = [] if 'managementvlan' in internal_config.keys(): managementvlan = internal_config['managementvlan'] - managementvlanports = internal_config['managementvlanbackupports'] + managementvlanbackupports = internal_config['managementvlanbackupports'] untaggedmanagementvlanports = [] if 'untaggedmanagementvlanports' in internal_config.keys(): untaggedmanagementvlanports = internal_config['untaggedmanagementvlanports'] @@ -710,21 +710,21 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): table = L2TUNNELTABLE mvrule = ManagementVLANLCRule(switch_id, managementvlan, - managementvlanports, + managementvlanbackupports, untaggedmanagementvlanports) results += self._translate_ManagementVLANLCRule(datapath, table, of_cookie, mvrule) - print "---------CW-------ADDING default flows" + self.logger.debug("---------CW-------ADDING backup management VLAN flows") # Install default rules for rule in results: self.add_flow(datapath, rule) def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule): '''Remove existing default port and use backup port''' - self.logger.debug("got ManagementLCRecoverRule-----CW---------") + self.logger.debug("got ManagementSDXRecoverRule-----CW---------") self.logger.debug("Checking if backup port is available") switch_id = 0 # This is unimportant: # it's never used in the translation @@ -744,9 +744,9 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule managementvlanports = internal_config['managementvlanports'] if 'sdxmanagementvlanbackupports' in internal_config.keys(): - managementvlanbackupports = internal_config['sdxmanagementvlanbackupports'] + sdxmanagementvlanbackupports = internal_config['sdxmanagementvlanbackupports'] else: - self.logger.debug("No backup port provided") + self.logger.debug("No SDX management VLAN backup port provided") #return #for table in ALL_TABLES_EXCEPT_LAST: @@ -805,7 +805,7 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule of_cookie, mvrule) - print "---------CW-------REMOVING default flows" + self.logger.debug("---------CW-------REMOVING default flows") # Install default rules for rule in results: self.remove_flow(datapath, rule) @@ -814,7 +814,7 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule results = [] if 'managementvlan' in internal_config.keys(): managementvlan = internal_config['managementvlan'] - managementvlanports = internal_config['managementvlanbackupports'] + sdxmanagementvlanbackupports = internal_config['sdxmanagementvlanbackupports'] untaggedmanagementvlanports = [] if 'untaggedmanagementvlanports' in internal_config.keys(): untaggedmanagementvlanports = internal_config['untaggedmanagementvlanports'] @@ -822,14 +822,14 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule table = L2TUNNELTABLE mvrule = ManagementVLANLCRule(switch_id, managementvlan, - managementvlanports, + sdxmanagementvlanbackupports, untaggedmanagementvlanports) results += self._translate_ManagementVLANLCRule(datapath, table, of_cookie, mvrule) - print "---------CW-------ADDING default flows" + self.logger.debug("---------CW-------ADDING SDX msg backup flows") # Install default rules for rule in results: self.add_flow(datapath, rule) From a9440809533cb2a875f8fe70e2e9b22a6ffdf5ad Mon Sep 17 00:00:00 2001 From: root Date: Mon, 6 Apr 2020 20:15:31 +0000 Subject: [PATCH 22/32] LC recovery is working now --- localctlr/LCRuleManager.py | 9 ++++++--- localctlr/LocalController.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/localctlr/LCRuleManager.py b/localctlr/LCRuleManager.py index 52e74d67..4c707754 100644 --- a/localctlr/LCRuleManager.py +++ b/localctlr/LCRuleManager.py @@ -67,9 +67,12 @@ def add_rule(self, cookie, switch_id, lcrule, for dupe in dupes: (c,sid,lcr,stat) = dupe if lcr == lcrule: - raise LCRuleManagerValidationError( - "Duplicate add_rule for %s:%s:%s" % - (cookie, switch_id, str(lcrule))) + if isinstance(ManagementLCRecoverRule): + self.logger.debug("ManagementLCRecoverRule, ignored.") + else: + raise LCRuleManagerValidationError( + "Duplicate add_rule for %s:%s:%s" % + (cookie, switch_id, str(lcrule))) # Translate rule into a string so it can be stored self.rule_table.insert({'cookie':cookie, diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index 19f945ac..b4424b72 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -683,7 +683,7 @@ def remove_rule_sdxmsg(self, msg): cookie = msg.get_data()['cookie'] rules = self.rm.get_rules(cookie, switch_id) - self.logger.debug("--- MCEVIK: remove_rule_sdxmsg - rules: %s" % (rules.__dict__)) + #self.logger.debug("--- MCEVIK: remove_rule_sdxmsg - rules: %s" % (rules.__dict__)) self.logger.debug("remove_rule_sdxmsg: %d:%s:%s" % (cookie, switch_id, From 3a43463fe4cb75b9c39594648d9de5724f6eeb60 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 10 Apr 2020 17:57:44 -0400 Subject: [PATCH 23/32] clean up debug messages --- localctlr/LocalController.py | 17 +--- localctlr/RyuTranslateInterface.py | 104 ++--------------------- sdxctlr/SDXController.py | 13 +-- shared/EdgePortPolicy.py | 4 - shared/ManagementSDXRecoverPolicy.py | 25 ++---- shared/SDXControllerConnectionManager.py | 4 - 6 files changed, 19 insertions(+), 148 deletions(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index b4424b72..754d910c 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -407,8 +407,6 @@ def _get_switch_internal_config_count(self): d = self.config_table.find() count = 0 for entry in d: - print "---------CW--ENTRY:-------------" - print entry if (entry['key'] == 'lcip' or entry['key'] == 'manifest_filename' or entry['key'] == 'ryucxnport'): @@ -444,8 +442,6 @@ def _get_config_filename_in_db(self): # Returns the manifest filename if it exists or None if it does not. key = 'manifest_filename' d = self.config_table.find_one(key=key) - print "------------_get_config_filename_in_db-----------" - print d if d == None: return None val = d['value'] @@ -546,8 +542,6 @@ def _setup(self, options): 'sdxport':self.sdxport}) # OpenFlow/Switch configuration data config_count = self._get_switch_internal_config_count() - print "------config_count------" - print config_count if config_count == 0: # Nothing configured, get configs from config file for entry in lcdata['switchinfo']: @@ -555,15 +549,11 @@ def _setup(self, options): ic = entry['internalconfig'] ic['name'] = entry['name'] self._add_switch_internal_config_to_db(dpid, ic) - + + # Hard coded, to be fixed internal_config = self._get_switch_internal_config(204) if internal_config == None: print "internal_config == None" - #if 'managementvlan' in internal_config.keys(): - # managementvlan = internal_config['managementvlan'] - #if 'managementvlanports' in internal_config.keys(): - # managementvlanports = internal_config['managementvlanports'] - #print managementvlan def start_sdx_controller_connection(self): # Kick off thread to start connection. @@ -631,7 +621,7 @@ def install_rule_sdxmsg(self, msg): switch_id = msg.get_switch_id() rule = msg cookie = msg.get_cookie() - self.logger.debug("Got ManagementLCRecoverRule-------CW-------") + self.logger.debug("Got ManagementLCRecoverRule, to be installed") else: switch_id = msg.get_data()['switch_id'] rule = msg.get_data()['rule'] @@ -651,7 +641,6 @@ def install_rule_sdxmsg(self, msg): self.rm.set_status(cookie, switch_id, RULE_STATUS_ACTIVE) def remove_all_rules_sdxmsg(self): - '''CW: this part is not working yet''' ''' Removes all rules based on cookie sent from the SDX Controller. ''' rules = self.rm.list_all_rules() diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index 4762b96c..24431d69 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -358,8 +358,6 @@ def _get_config_filename_in_db(self): # Returns the manifest filename if it exists or None if it does not. key = 'manifest_filename' d = self.config_table.find_one(key=key) - print "cw-RyuTranslate-_get_config_filename_in_db" - print d if d == None: return None val = d['value'] @@ -490,15 +488,6 @@ def switch_features_handler(self, ev): # Call bootstrapping for switch functions self._new_switch_bootstrapping(ev) - # Handles port status change event - #@set_ev_cls(ofp_event.EventOFPPortStatus, CONFIG_DISPATCHER) - #def port_status_handler(self, ev): - # self.logger.warning("Port status changed: connection from: " + str(ev.msg.datapath.id) + " for " + str(self)) - # self.datapaths[ev.msg.datapath.id] = ev.msg.datapath - - # Call backup port recovery for switch functions - # self._backup_port_recover(ev) - # From the Ryu mailing list: https://sourceforge.net/p/ryu/mailman/message/33584125/ @set_ev_cls(ofp_event.EventOFPErrorMsg, [CONFIG_DISPATCHER, MAIN_DISPATCHER]) @@ -531,8 +520,6 @@ def _new_switch_bootstrapping(self, ev): switch_id = 0 # This is unimportant: # it's never used in the translation datapath = ev.msg.datapath - #print "----------_new_switch_bootstrapping----------CW----------dpid:" - #print datapath self.remove_all_flows(datapath) @@ -612,7 +599,7 @@ def _new_switch_bootstrapping(self, ev): def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): '''Remove existing default port and use backup port''' - self.logger.debug("got ManagementLCRecoverRule-----CW---------") + self.logger.debug("got ManagementLCRecoverRule") self.logger.debug("Checking if backup port is available") switch_id = 0 # This is unimportant: # it's never used in the translation @@ -635,44 +622,7 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): managementvlanbackupports = internal_config['managementvlanbackupports'] else: self.logger.debug("No backup port provided") - #return - - #for table in ALL_TABLES_EXCEPT_LAST: - # matches = [] # FIXME: what's the equivalent of match(*)? - # actions = [Continue()] - # priority = PRIORITY_DEFAULT - # marule = MatchActionLCRule(switch_id, matches, actions) - # results += self._translate_MatchActionLCRule(datapath, - # table, - # of_cookie, - # marule, - # priority) - - # For last table - # - Create a default drop rule (if necessary needed). Priority 0 - #for i in (managementvlanports): - # self.logger.debug("Using default management ports.") - # matches = [IN_PORT(i)] - # actions = [Drop()] - # priority = PRIORITY_DEFAULT_PLUS_ONE - # table = LASTTABLE - # marule = MatchActionLCRule(switch_id, matches, actions) - # results += self._translate_MatchActionLCRule(datapath, - # table, - # of_cookie, - # marule, - # priority) - # Catch-all for those not in the same port - #matches = [] - #actions = [Drop()] - #priority = PRIORITY_DEFAULT - #table = LASTTABLE - #marule = MatchActionLCRule(switch_id, matches, actions) - #results += self._translate_MatchActionLCRule(datapath, - # table, - # of_cookie, - # marule, - # priority) + return # In-band Communication # If the management VLAN needs to be setup, set it up. @@ -693,7 +643,7 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): of_cookie, mvrule) - self.logger.debug("---------CW-------REMOVING default flows") + self.logger.debug("REMOVING default flows") # Install default rules for rule in results: self.remove_flow(datapath, rule) @@ -717,14 +667,14 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): of_cookie, mvrule) - self.logger.debug("---------CW-------ADDING backup management VLAN flows") + self.logger.debug("ADDING backup management VLAN flows") # Install default rules for rule in results: self.add_flow(datapath, rule) def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule): '''Remove existing default port and use backup port''' - self.logger.debug("got ManagementSDXRecoverRule-----CW---------") + self.logger.debug("got ManagementSDXRecoverRule from SDX message") self.logger.debug("Checking if backup port is available") switch_id = 0 # This is unimportant: # it's never used in the translation @@ -747,44 +697,7 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule sdxmanagementvlanbackupports = internal_config['sdxmanagementvlanbackupports'] else: self.logger.debug("No SDX management VLAN backup port provided") - #return - - #for table in ALL_TABLES_EXCEPT_LAST: - # matches = [] # FIXME: what's the equivalent of match(*)? - # actions = [Continue()] - # priority = PRIORITY_DEFAULT - # marule = MatchActionLCRule(switch_id, matches, actions) - # results += self._translate_MatchActionLCRule(datapath, - # table, - # of_cookie, - # marule, - # priority) - - # For last table - # - Create a default drop rule (if necessary needed). Priority 0 - #for i in (managementvlanports): - # self.logger.debug("Using default management ports.") - # matches = [IN_PORT(i)] - # actions = [Drop()] - # priority = PRIORITY_DEFAULT_PLUS_ONE - # table = LASTTABLE - # marule = MatchActionLCRule(switch_id, matches, actions) - # results += self._translate_MatchActionLCRule(datapath, - # table, - # of_cookie, - # marule, - # priority) - # Catch-all for those not in the same port - #matches = [] - #actions = [Drop()] - #priority = PRIORITY_DEFAULT - #table = LASTTABLE - #marule = MatchActionLCRule(switch_id, matches, actions) - #results += self._translate_MatchActionLCRule(datapath, - # table, - # of_cookie, - # marule, - # priority) + return # In-band Communication # If the management VLAN needs to be setup, set it up. @@ -805,7 +718,7 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule of_cookie, mvrule) - self.logger.debug("---------CW-------REMOVING default flows") + self.logger.debug("REMOVING default flows") # Install default rules for rule in results: self.remove_flow(datapath, rule) @@ -829,7 +742,7 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule of_cookie, mvrule) - self.logger.debug("---------CW-------ADDING SDX msg backup flows") + self.logger.debug("ADDING SDX msg backup flows") # Install default rules for rule in results: self.add_flow(datapath, rule) @@ -1824,7 +1737,6 @@ def install_rule(self, datapath, sdx_rule): return elif isinstance(sdx_rule, ManagementSDXRecoverRule): - print "Got ManagementSDXRecoverRule---------CW----------" self._backup_port_recover_from_sdx_msg(datapath, of_cookie, sdx_rule) return diff --git a/sdxctlr/SDXController.py b/sdxctlr/SDXController.py index 06861bf8..543f1d77 100644 --- a/sdxctlr/SDXController.py +++ b/sdxctlr/SDXController.py @@ -201,16 +201,9 @@ def _handle_new_connection(self, cxn): topo = self.tm.get_topology() self.logger.warning("New connection - name %s details %s" % (name, cxn)) for switch in topo.node[name]['switches']: - print "-----------CW--------SDXController: for switch in topo.node[name]['switches']:------------" - print switch json_rule = {"EdgePort":{"switch":switch}} epp = EdgePortPolicy(AUTOGENERATED_USERNAME, json_rule) - #json_rule = {"ManagementSDXRecover":{"switch":switch}} - #msr = ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule) - #print "-----------CW--------SDXController:ManagementSDXRecoverPolicy(AUTOGENERATED_USERNAME, json_rule)-------" - #print json_rule self.rm.add_rule(epp) - #self.rm.add_rule(msr) def _handle_connection_loss(self, cxn): #FIXME: Send this to the LocalControllerManager @@ -221,8 +214,7 @@ def _handle_connection_loss(self, cxn): # Get LC name name = cxn.get_name() - print "------------CW-----------SDXController: _handle_connection_loss(self, cxn):name = cxn.get_name()-------" - print name + self.logger.debug("Local Controller Lost connection: " + str(name)) # Delete connections associations self.sdx_cm.dissociate_name_with_cxn(name) # Get all EdgePort policies @@ -230,8 +222,9 @@ def _handle_connection_loss(self, cxn): # for each switch owned by a particular LC, delete that particular rule topo = self.tm.get_topology() - print "------------CW-----------internalconfig----------" + self.logger.debug("Getting backup LC.") backuplc = topo.node[name]['internalconfig']['backuplc'] + self.logger.debug("Got backup LC: " + str(backuplc)) for switch in topo.node[name]['switches']: json_rule = {"EdgePort":{"switch":switch}} diff --git a/shared/EdgePortPolicy.py b/shared/EdgePortPolicy.py index 89936e08..1a9290d3 100644 --- a/shared/EdgePortPolicy.py +++ b/shared/EdgePortPolicy.py @@ -68,11 +68,7 @@ def breakdown_rule(self, tm, ai): topology = tm.get_topology() authorization_func = ai.is_authorized switch_id = topology.node[self.switch]['dpid'] - print "---------CW-------EdgePortPolicy------switch_id = topology.node[self.switch]['dpid']------------" - print switch_id shortname = topology.node[self.switch]['locationshortname'] - print "---------CW-------EdgePortPolicy------shortname = topology.node[self.switch]['locationshortname']------------" - print shortname bd = UserPolicyBreakdown(shortname, []) diff --git a/shared/ManagementSDXRecoverPolicy.py b/shared/ManagementSDXRecoverPolicy.py index 20acdb6c..f2194dac 100644 --- a/shared/ManagementSDXRecoverPolicy.py +++ b/shared/ManagementSDXRecoverPolicy.py @@ -10,22 +10,14 @@ import networkx as nx class ManagementSDXRecoverPolicy(UserPolicy): - ''' This policy is used during initialization of the LocalController to let - it know which ports of it are edge ports. This is necessary for proper - learning of new paths to occur without redundant "new destination" - messages coming in from switch-to-switch ports. - - It requires the following information at intialization: + ''' This policy is used by SDX to try covering connection once LocalController + connection is lost (heart beat is missing). + It requires the following information: - Switch - Example Json: {"EdgePort":{ "switch":"mia-switch"}} - - The vast majority of the work is handled by the breakdown_rule() function - which uses the topology to determine which ports are actually the edge - ports. - ''' + ''' def __init__(self, username, json_rule): self.switch = None @@ -64,7 +56,7 @@ def breakdown_rule(self, tm, ai): what type the neighbor is. If they are a "switch" type, then that's an internal port, otherwise, it's an edge port. ''' - print "-----CW-breakdown_rule(self, tm, ai):-------" + self.breakdown = [] topology = tm.get_topology() authorization_func = ai.is_authorized @@ -73,9 +65,6 @@ def breakdown_rule(self, tm, ai): bd = UserPolicyBreakdown(shortname, []) msr = ManagementSDXRecoverRule(switch_id) bd.add_to_list_of_rules(msr) - - for i in bd.get_list_of_rules(): - print self.breakdown.append(bd) return self.breakdown @@ -88,10 +77,6 @@ def check_validity(self, tm, ai): def _parse_json(self, json_rule): jsonstring = self.ruletype - #print "rule type:" + str(jsonstring) - #print "json_rule.keys():" - #for i in json_rule.keys(): - # print i if type(json_rule) is not dict: raise UserPolicyTypeError("json_rule is not a dictionary:\n %s" % json_rule) if jsonstring not in json_rule.keys(): diff --git a/shared/SDXControllerConnectionManager.py b/shared/SDXControllerConnectionManager.py index 9dcb7908..c8f428a7 100644 --- a/shared/SDXControllerConnectionManager.py +++ b/shared/SDXControllerConnectionManager.py @@ -63,10 +63,6 @@ def send_breakdown_rule_add(self, bd): for rule in bd.get_list_of_rules(): switch_id = rule.get_switch_id() msg = SDXMessageInstallRule(rule, switch_id) - print "---------CW send_breakdown_rule_add---------msg------------" - print msg - print "---------CW send_breakdown_rule_add---------rule-----------" - print rule lc_cxn.send_protocol(msg) except SDXControllerConnectionManagerNotConnectedError as e: From 5a185cd842a79738ff124421d136254d95390554 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 10 Apr 2020 22:02:15 -0400 Subject: [PATCH 24/32] remove debug messages --- sdxctlr/RuleManager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdxctlr/RuleManager.py b/sdxctlr/RuleManager.py index 1e4171ed..46bf6517 100644 --- a/sdxctlr/RuleManager.py +++ b/sdxctlr/RuleManager.py @@ -558,12 +558,8 @@ def _install_breakdown(self, breakdown): try: for bd in breakdown: self.logger.debug("Sending install breakdown: %s" % bd) - print "before bd.get_list_of_rules()" for rule in bd.get_list_of_rules(): - print "rule:" - print rule self.logger.debug(" %s" % str(rule)) - print "after bd.get_list_of_rules()" self.send_user_add_rule(bd) except Exception as e: raise From 4ac03e1aff6305b4eff051bf5622f4db0291b727 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 11 Apr 2020 02:18:42 +0000 Subject: [PATCH 25/32] remove debug messages --- localctlr/LocalController.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index 754d910c..ca55abc6 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -168,8 +168,6 @@ def _main_loop(self): except Exception as e: self.logger.error("LocalController: Error in select - %s" % (e)) - #lc_recover = ManagementLCRecoverRule(0, 204) - #self.install_rule_sdxmsg(lc_recover) # Loop through readable for entry in readable: # Get Message @@ -259,8 +257,6 @@ def _main_loop(self): # Restart new connection # self.start_sdx_controller_connection() # sleep(5) - lc_recover = ManagementLCRecoverRule(0, 204) - self.install_rule_sdxmsg(recover) self.start_sdx_controller_connection() #num_of_retry = 0 @@ -551,9 +547,9 @@ def _setup(self, options): self._add_switch_internal_config_to_db(dpid, ic) # Hard coded, to be fixed - internal_config = self._get_switch_internal_config(204) - if internal_config == None: - print "internal_config == None" + #internal_config = self._get_switch_internal_config(204) + #if internal_config == None: + # print "internal_config == None" def start_sdx_controller_connection(self): # Kick off thread to start connection. From b832c30d0ff6f7c7abea49e100be57e17e8152e6 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 11 Apr 2020 02:56:53 +0000 Subject: [PATCH 26/32] read dpid from lc config --- localctlr/LocalController.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index ca55abc6..0db101f0 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -152,14 +152,17 @@ def _main_loop(self): if (self.sdx_connection == None and self.start_cxn_thread == None): self.logger.info("Restarting SDX Connection") - lc_recover = ManagementLCRecoverRule(0, 204) - self.install_rule_sdxmsg(lc_recover) + for entry in self.lcconfigdata['switchinfo']: + dpid = int(entry['dpid'], 0) + lc_recover = ManagementLCRecoverRule(0, dpid) + self.install_rule_sdxmsg(lc_recover) + self.logger.debug("ManagementLCRecoverRule sent. About to restart SDX connection.") self.start_sdx_controller_connection() #Restart! if len(rlist) == 0: sleep(timeout/2) continue - + try: readable, writable, exceptional = cxnselect(rlist, wlist, @@ -475,6 +478,7 @@ def _setup(self, options): (str(self.conf_file), str(self._get_config_filename_in_db()))) + self.lcconfigdata = None # Get config file, if it exists try: self.logger.info("Opening config file %s" % self.conf_file) @@ -491,6 +495,8 @@ def _setup(self, options): with open(self.manifest) as data_file: data = json.load(data_file) lcdata = data['localcontrollers'][self.name] + # Save lcdata for future use + self.lcconfigdata = lcdata self.logger.info("Successfully opened manifest file %s" % self.manifest) except Exception as e: From 850a090258cea24017ca7e50a63da0957faae5c2 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 10 Apr 2020 23:28:55 -0400 Subject: [PATCH 27/32] remove unused debug --- localctlr/LCRuleManager.py | 2 +- localctlr/LocalController.py | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/localctlr/LCRuleManager.py b/localctlr/LCRuleManager.py index 4c707754..110c3dc7 100644 --- a/localctlr/LCRuleManager.py +++ b/localctlr/LCRuleManager.py @@ -130,7 +130,7 @@ def _find_rules(self, filter={}): def list_all_rules(self, full_tuple=False): rules = self.rule_table.find() - self.logger.debug("---------------cw:Retrieving all rules--------------.") + self.logger.debug("Retrieving all rules.") if full_tuple: retval = [(x['cookie'], x['switch_id'], diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index 0db101f0..c2633985 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -214,7 +214,6 @@ def _main_loop(self): self.logger.debug("Received a INSTL message from %s" % hex(id(entry))) self.install_rule_sdxmsg(msg) - #self.remove_all_rules_sdxmsg() # If RemoveRule elif type(msg) == SDXMessageRemoveRule: @@ -256,13 +255,8 @@ def _main_loop(self): self.sdx_connection.close() self.sdx_connection = None self.cxn_q.put((DEL_CXN, cxn)) - #if num_of_retry <= 5: - # Restart new connection - # self.start_sdx_controller_connection() - # sleep(5) - self.start_sdx_controller_connection() - #num_of_retry = 0 - + + self.start_sdx_controller_connection() def _add_switch_config_to_db(self, switch_name, switch_config): @@ -551,11 +545,6 @@ def _setup(self, options): ic = entry['internalconfig'] ic['name'] = entry['name'] self._add_switch_internal_config_to_db(dpid, ic) - - # Hard coded, to be fixed - #internal_config = self._get_switch_internal_config(204) - #if internal_config == None: - # print "internal_config == None" def start_sdx_controller_connection(self): # Kick off thread to start connection. @@ -635,8 +624,6 @@ def install_rule_sdxmsg(self, msg): self.rm.add_rule(cookie, switch_id, rule, RULE_STATUS_INSTALLING) self.switch_connection.send_command(switch_id, rule) - #self.rm.list_all_rules(True) - #self.logger.debug("----------self.rm.list_all_rules complete---------------") #FIXME: This should be moved to somewhere where we have positively #confirmed a rule has been installed. Right now, there is no such #location as the LC/RyuTranslateInteface protocol is primitive. From f8233d4f7f712778bb85c7324ac264e16a0018f6 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 10 Apr 2020 23:36:24 -0400 Subject: [PATCH 28/32] fix some comments --- localctlr/LocalController.py | 1 + shared/ManagementLCRecoverRule.py | 7 ++---- shared/ManagementSDXRecoverPolicy.py | 10 +------- shared/ManagementSDXRecoverRule.py | 34 +++------------------------- 4 files changed, 7 insertions(+), 45 deletions(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index c2633985..9da662d5 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -256,6 +256,7 @@ def _main_loop(self): self.sdx_connection = None self.cxn_q.put((DEL_CXN, cxn)) + # Restart new connection self.start_sdx_controller_connection() diff --git a/shared/ManagementLCRecoverRule.py b/shared/ManagementLCRecoverRule.py index b6a516a8..eb2112e1 100644 --- a/shared/ManagementLCRecoverRule.py +++ b/shared/ManagementLCRecoverRule.py @@ -4,15 +4,12 @@ from LCRule import * class ManagementLCRecoverRule(LCRule): - ''' This structure is used to pass the ports that belong to a spanning tree - to the Local Controller for handling broadcast flooding without cycles. - Created by FloodTreePolicy. ''' + ''' This structure is used to pass the Management VLAN recover rule to + RyuTranslateInterface. This rule is created just for trigger recovery. ''' def __init__(self, cookie, switch_id): ''' Field descritpions: switch_id - Which switch is involved - ports - List of ports that are part of the spanning tree needed - for broadcast flooding. ''' super(ManagementLCRecoverRule, self).__init__(self, switch_id) self.cookie = cookie diff --git a/shared/ManagementSDXRecoverPolicy.py b/shared/ManagementSDXRecoverPolicy.py index f2194dac..70bcd69e 100644 --- a/shared/ManagementSDXRecoverPolicy.py +++ b/shared/ManagementSDXRecoverPolicy.py @@ -48,15 +48,7 @@ def check_syntax(cls, json_rule): str(e), filename,lineno) raise - def breakdown_rule(self, tm, ai): - ''' There are two stages to breaking down these rules: - - determine edge ports for the local switch - - create EdgePortLCRules for each edge port - To determine which ports are edge port, we look at each port and see - what type the neighbor is. If they are a "switch" type, then that's - an internal port, otherwise, it's an edge port. - ''' - + def breakdown_rule(self, tm, ai): self.breakdown = [] topology = tm.get_topology() authorization_func = ai.is_authorized diff --git a/shared/ManagementSDXRecoverRule.py b/shared/ManagementSDXRecoverRule.py index 502fc4b5..f7d97ce3 100644 --- a/shared/ManagementSDXRecoverRule.py +++ b/shared/ManagementSDXRecoverRule.py @@ -3,44 +3,16 @@ from LCRule import * -# Some noteson the current implementation: -# - This version is a first pass at implementing the Management VLAN. There are -# a number of features that are skipped or implemented differently. -# - This version, like other parts of the controller, is not resillient in -# the face of failures. That is, a link failure may take down the -# management VLAN if that link was integral to the Management VLAN spanning -# tree. -# - Spanning Tree is necessary to avoid cycles. Cycles are *bad*. -# - The Spanning Tree that is used is manually configured in the manifest. -# - All messages are flooded -# - Future changes that are necessary: -# - Implement localized Spanning Tree Protocol for Management VLAN -# - This need not be 802.1 STP, but we need to be able to distributedly -# build a spanning tree. -# - When Spanning Tree is rebuilt due to failure, Updates to the rules are -# needed -# - Flooding should be updated to be learned. This probably will need to be -# internal to the LC. -# - This would require certain updates to the RyuTranslateInterface to handle -# all this new functionality. -# - Learning -# - Connect/disconnect - -# Copyright 2017 - Sean Donovan -# AtlanticWave/SDX Project - from LCRule import * class ManagementSDXRecoverRule(LCRule): - ''' This structure is used to pass the ports that belong to a spanning tree - to the Local Controller for handling broadcast flooding without cycles. - Created by FloodTreePolicy. ''' + ''' This rule by SDX to try covering connection once LocalController + connection is lost (heart beat is missing). + Created by ManagementSDXRecoverPolicy. ''' def __init__(self, switch_id): ''' Field descritpions: switch_id - Which switch is involved - ports - List of ports that are part of the spanning tree needed - for broadcast flooding. ''' self.switch_id = switch_id super(ManagementSDXRecoverRule, self).__init__(switch_id) From e61c69cd3cb28be68f09508fe18382fc011542d5 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 10 Apr 2020 23:46:20 -0400 Subject: [PATCH 29/32] minor fix on comments --- localctlr/LocalController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localctlr/LocalController.py b/localctlr/LocalController.py index 9da662d5..98551d08 100644 --- a/localctlr/LocalController.py +++ b/localctlr/LocalController.py @@ -631,7 +631,7 @@ def install_rule_sdxmsg(self, msg): self.rm.set_status(cookie, switch_id, RULE_STATUS_ACTIVE) def remove_all_rules_sdxmsg(self): - ''' Removes all rules based on cookie sent from the SDX Controller. ''' + ''' Removes all data plane rules. ''' rules = self.rm.list_all_rules() if rules == []: From b6d8dff3c348f8d2ff8c1629a59cd0f58d3e2b53 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 13 Apr 2020 04:04:16 +0000 Subject: [PATCH 30/32] fix _get_new_OF_cookie argument --- localctlr/RyuTranslateInterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localctlr/RyuTranslateInterface.py b/localctlr/RyuTranslateInterface.py index ce566821..d3882c6d 100644 --- a/localctlr/RyuTranslateInterface.py +++ b/localctlr/RyuTranslateInterface.py @@ -613,7 +613,7 @@ def _backup_port_recover(self, datapath, of_cookie, lc_recover_rule): switch_id = 0 # This is unimportant: # it's never used in the translation - of_cookie = self._get_new_OF_cookie(-1) # FIXME: magic number + of_cookie = self._get_new_OF_cookie(-1, -1) # FIXME: magic number results = [] # In-band Communication @@ -688,7 +688,7 @@ def _backup_port_recover_from_sdx_msg(self, datapath, of_cookie, lc_recover_rule switch_id = 0 # This is unimportant: # it's never used in the translation - of_cookie = self._get_new_OF_cookie(-1) # FIXME: magic number + of_cookie = self._get_new_OF_cookie(-1, -1) # FIXME: magic number results = [] # In-band Communication From fdc2e7081694bef10a7e5c44282879931b78e6d8 Mon Sep 17 00:00:00 2001 From: Yufeng Xin Date: Mon, 13 Apr 2020 09:42:39 -0400 Subject: [PATCH 31/32] build on the develop branch --- configuration/helloworld-multi-vagrant/vagrant-setup.sh | 2 +- docker/lc_container/Dockerfile | 2 +- docker/sdx_container/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configuration/helloworld-multi-vagrant/vagrant-setup.sh b/configuration/helloworld-multi-vagrant/vagrant-setup.sh index 1c2e25ec..32fe1a5b 100644 --- a/configuration/helloworld-multi-vagrant/vagrant-setup.sh +++ b/configuration/helloworld-multi-vagrant/vagrant-setup.sh @@ -9,7 +9,7 @@ sudo usermod -aG docker $USER # git work -git clone -b mcevik-l2multipoint-ratelimiting https://github.com/atlanticwave-sdx/atlanticwave-proto.git +git clone -b develop https://github.com/atlanticwave-sdx/atlanticwave-proto.git # Docker work: build SDX Controller and Local Controller containers cd ~/atlanticwave-proto/ diff --git a/docker/lc_container/Dockerfile b/docker/lc_container/Dockerfile index 487eed8f..fa9ed2ed 100644 --- a/docker/lc_container/Dockerfile +++ b/docker/lc_container/Dockerfile @@ -3,7 +3,7 @@ FROM ubuntu:16.04 RUN apt update && apt install -y git python-virtualenv pypy python-pip net-tools vim # This really should point to a particular version -RUN git clone -b mcevik-l2multipoint-ratelimiting https://github.com/atlanticwave-sdx/atlanticwave-proto.git +RUN git clone -b develop https://github.com/atlanticwave-sdx/atlanticwave-proto.git # Virtualenv setup RUN virtualenv -p /usr/bin/pypy /appenv diff --git a/docker/sdx_container/Dockerfile b/docker/sdx_container/Dockerfile index a4cc8991..216a19e4 100644 --- a/docker/sdx_container/Dockerfile +++ b/docker/sdx_container/Dockerfile @@ -14,7 +14,7 @@ COPY run_sdx.sh . COPY ./*.manifest ./ # This really should point to a particular version -RUN git clone -b mcevik-l2multipoint-ratelimiting https://github.com/atlanticwave-sdx/atlanticwave-proto.git +RUN git clone -b develop https://github.com/atlanticwave-sdx/atlanticwave-proto.git RUN cat atlanticwave-proto/requirements.txt RUN pip install -r atlanticwave-proto/requirements.txt From 1b10eed590d555fc07aee0e625450cc86fafab25 Mon Sep 17 00:00:00 2001 From: Yufeng Xin Date: Mon, 13 Apr 2020 10:49:03 -0400 Subject: [PATCH 32/32] build on the master branch --- configuration/helloworld-multi-vagrant/vagrant-setup.sh | 2 +- docker/lc_container/Dockerfile | 2 +- docker/sdx_container/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configuration/helloworld-multi-vagrant/vagrant-setup.sh b/configuration/helloworld-multi-vagrant/vagrant-setup.sh index 32fe1a5b..e7bde753 100644 --- a/configuration/helloworld-multi-vagrant/vagrant-setup.sh +++ b/configuration/helloworld-multi-vagrant/vagrant-setup.sh @@ -9,7 +9,7 @@ sudo usermod -aG docker $USER # git work -git clone -b develop https://github.com/atlanticwave-sdx/atlanticwave-proto.git +git clone -b master https://github.com/atlanticwave-sdx/atlanticwave-proto.git # Docker work: build SDX Controller and Local Controller containers cd ~/atlanticwave-proto/ diff --git a/docker/lc_container/Dockerfile b/docker/lc_container/Dockerfile index fa9ed2ed..e906f2ad 100644 --- a/docker/lc_container/Dockerfile +++ b/docker/lc_container/Dockerfile @@ -3,7 +3,7 @@ FROM ubuntu:16.04 RUN apt update && apt install -y git python-virtualenv pypy python-pip net-tools vim # This really should point to a particular version -RUN git clone -b develop https://github.com/atlanticwave-sdx/atlanticwave-proto.git +RUN git clone -b master https://github.com/atlanticwave-sdx/atlanticwave-proto.git # Virtualenv setup RUN virtualenv -p /usr/bin/pypy /appenv diff --git a/docker/sdx_container/Dockerfile b/docker/sdx_container/Dockerfile index 216a19e4..411ca3fc 100644 --- a/docker/sdx_container/Dockerfile +++ b/docker/sdx_container/Dockerfile @@ -14,7 +14,7 @@ COPY run_sdx.sh . COPY ./*.manifest ./ # This really should point to a particular version -RUN git clone -b develop https://github.com/atlanticwave-sdx/atlanticwave-proto.git +RUN git clone -b master https://github.com/atlanticwave-sdx/atlanticwave-proto.git RUN cat atlanticwave-proto/requirements.txt RUN pip install -r atlanticwave-proto/requirements.txt