From ee71c7c7ec83582e97aa6e6ad6d9d35f59083b99 Mon Sep 17 00:00:00 2001 From: dpaks Date: Fri, 7 Apr 2017 13:59:02 +0530 Subject: [PATCH 01/16] A working bidirectional SFC for TAP devices. 1. Achieved using a FC with both LSP and LDP. - Treated as a bidirectional chain 2. Supports TAP/IDS devices. --- networking_sfc/db/flowclassifier_db.py | 4 +- .../flowclassifier/drivers/oc/__init__.py | 0 .../flowclassifier/drivers/oc/driver.py | 42 +++ .../flowclassifier/drivers/ovs/driver.py | 15 +- .../extensions/openvswitch/sfc_driver.py | 306 +++++++++++++++++- .../services/sfc/drivers/oc/__init__.py | 0 .../services/sfc/drivers/oc/driver.py | 42 +++ .../services/sfc/drivers/ovs/driver.py | 194 ++++++++--- 8 files changed, 535 insertions(+), 68 deletions(-) create mode 100644 networking_sfc/services/flowclassifier/drivers/oc/__init__.py create mode 100644 networking_sfc/services/flowclassifier/drivers/oc/driver.py create mode 100644 networking_sfc/services/sfc/drivers/oc/__init__.py create mode 100644 networking_sfc/services/sfc/drivers/oc/driver.py diff --git a/networking_sfc/db/flowclassifier_db.py b/networking_sfc/db/flowclassifier_db.py index 91e4bcce..e3085958 100644 --- a/networking_sfc/db/flowclassifier_db.py +++ b/networking_sfc/db/flowclassifier_db.py @@ -111,8 +111,8 @@ def _check_ip_prefix_valid(cls, ip_prefix, ethertype): @classmethod def _logical_port_conflict(cls, first_logical_port, second_logical_port): - if first_logical_port is None or second_logical_port is None: - return True + '''if first_logical_port is None or second_logical_port is None: + return True''' return first_logical_port == second_logical_port @classmethod diff --git a/networking_sfc/services/flowclassifier/drivers/oc/__init__.py b/networking_sfc/services/flowclassifier/drivers/oc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/networking_sfc/services/flowclassifier/drivers/oc/driver.py b/networking_sfc/services/flowclassifier/drivers/oc/driver.py new file mode 100644 index 00000000..e02a81c2 --- /dev/null +++ b/networking_sfc/services/flowclassifier/drivers/oc/driver.py @@ -0,0 +1,42 @@ +from oslo_log import helpers as log_helpers + +from networking_sfc.services.flowclassifier.common import exceptions as exc +from networking_sfc.services.flowclassifier.drivers import base as fc_driver + + +class OVSFlowClassifierDriver(fc_driver.FlowClassifierDriverBase): + """FlowClassifier Driver Base Class.""" + + def initialize(self): + pass + + @log_helpers.log_method_call + def create_flow_classifier(self, context): + pass + + @log_helpers.log_method_call + def update_flow_classifier(self, context): + pass + + @log_helpers.log_method_call + def delete_flow_classifier(self, context): + pass + + @log_helpers.log_method_call + def create_flow_classifier_precommit(self, context): + """OVS Driver precommit before transaction committed. + + Make sure the logical_source_port is not None. + Make sure the logical_destination_port is None. + """ + flow_classifier = context.current + logical_source_port = flow_classifier['logical_source_port'] + if logical_source_port is None: + raise exc.FlowClassifierBadRequest(message=( + 'FlowClassifier %s does not set ' + 'logical source port in ovs driver' % flow_classifier['id'])) + logical_destination_port = flow_classifier['logical_destination_port'] + if logical_destination_port is not None: + raise exc.FlowClassifierBadRequest(message=( + 'FlowClassifier %s sets logical destination port ' + 'in ovs driver' % flow_classifier['id'])) diff --git a/networking_sfc/services/flowclassifier/drivers/ovs/driver.py b/networking_sfc/services/flowclassifier/drivers/ovs/driver.py index 4a1bf9ac..8182f49e 100644 --- a/networking_sfc/services/flowclassifier/drivers/ovs/driver.py +++ b/networking_sfc/services/flowclassifier/drivers/ovs/driver.py @@ -40,17 +40,14 @@ def delete_flow_classifier(self, context): def create_flow_classifier_precommit(self, context): """OVS Driver precommit before transaction committed. - Make sure the logical_source_port is not None. - Make sure the logical_destination_port is None. + Make sure that either the logical_source_port + or the logical_destination_port is not None. """ + flow_classifier = context.current logical_source_port = flow_classifier['logical_source_port'] - if logical_source_port is None: - raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s does not set ' - 'logical source port in ovs driver' % flow_classifier['id'])) logical_destination_port = flow_classifier['logical_destination_port'] - if logical_destination_port is not None: + if (logical_source_port or logical_destination_port) is None: raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s sets logical destination port ' - 'in ovs driver' % flow_classifier['id'])) + 'FlowClassifier %s requires either logical destination port or' + ' logical source port in ovs driver' % flow_classifier['id'])) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index 031670c4..5e337ea6 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import copy + from neutron_lib import constants as n_consts from oslo_config import cfg from oslo_log import log as logging @@ -95,8 +97,10 @@ def update_flow_rules(self, flowrule, flowrule_status): try: if flowrule.get('egress', None): self._setup_egress_flow_rules(flowrule) + self._setup_reverse_ingress_flow_rules(flowrule) if flowrule.get('ingress', None): self._setup_ingress_flow_rules_with_mpls(flowrule) + self._setup_reverse_egress_flow_rules(flowrule) flowrule_status_temp = {} flowrule_status_temp['id'] = flowrule['id'] @@ -115,6 +119,7 @@ def delete_flow_rule(self, flowrule, flowrule_status): LOG.debug("delete_flow_rule, flowrule = %s", flowrule) + node_type = flowrule['node_type'] # delete tunnel table flow rule on br-int(egress match) if flowrule['egress'] is not None: self._setup_local_switch_flows_on_int_br( @@ -124,6 +129,8 @@ def delete_flow_rule(self, flowrule, flowrule_status): add_flow=False, match_inport=True ) + self._setup_source_based_flows( + flowrule, flowrule['del_fcs'], add_flow=False) # delete group table, need to check again group_id = flowrule.get('next_group_id', None) if group_id and flowrule.get('group_refcnt', None) <= 1: @@ -134,6 +141,9 @@ def delete_flow_rule(self, flowrule, flowrule_status): dl_dst=item['mac_address']) if flowrule['ingress'] is not None: + self._setup_destination_based_forwarding(flowrule, + flowrule['del_fcs'], + add_flow=False) # delete table INGRESS_TABLE ingress match flow rule # on br-int(ingress match) vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) @@ -146,6 +156,16 @@ def delete_flow_rule(self, flowrule, flowrule_status): dl_dst=vif_port.vif_mac, mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1) ) + if node_type in ['bi_node', 'sf_bi_node']: + rev_flowrule = self._reverse_flow_rules( + flowrule, flowrule['node_type']) + if flowrule['ingress'] is not None: + self._setup_source_based_flows( + rev_flowrule, rev_flowrule['del_fcs'], add_flow=False) + else: + self._setup_destination_based_forwarding( + rev_flowrule, rev_flowrule['del_fcs'], + add_flow=False) except Exception as e: flowrule_status_temp = {} flowrule_status_temp['id'] = flowrule['id'] @@ -154,9 +174,46 @@ def delete_flow_rule(self, flowrule, flowrule_status): LOG.exception(e) LOG.error(_LE("delete_flow_rule failed")) + def _reverse_flow_rules(self, flowrule, node_type): + rev_flowrule = copy.deepcopy(flowrule) + + def _reverse_fcs(op): + for fc in rev_flowrule[op]: + fc['logical_destination_port'], fc['logical_source_port'] = ( + fc['logical_source_port'], fc['logical_destination_port']) + fc['ldp_mac_address'], fc['lsp_mac_address'] = ( + fc['lsp_mac_address'], fc['ldp_mac_address']) + fc['destination_ip_prefix'], fc['source_ip_prefix'] = ( + fc['source_ip_prefix'], fc['destination_ip_prefix']) + + for op in ['add_fcs', 'del_fcs']: + _reverse_fcs(op) + + if node_type == 'bi_node': + rev_flowrule['ingress'], rev_flowrule['egress'] = ( + rev_flowrule['egress'], rev_flowrule['ingress']) + + return rev_flowrule + + def _setup_reverse_ingress_flow_rules(self, flowrule): + node_type = flowrule['node_type'] + if node_type not in ['bi_node', 'sf_bi_node']: + return + rev_flowrule = self._reverse_flow_rules(flowrule, node_type) + self._setup_ingress_flow_rules_with_mpls(rev_flowrule) + + def _setup_reverse_egress_flow_rules(self, flowrule): + node_type = flowrule['node_type'] + if node_type not in ['bi_node', 'sf_bi_node']: + return + rev_flowrule = self._reverse_flow_rules(flowrule, node_type) + self._setup_egress_flow_rules(rev_flowrule) + def _clear_sfc_flow_on_int_br(self): self.br_int.delete_group(group_id='all') self.br_int.delete_flows(table=ACROSS_SUBNET_TABLE) + self.br_int.delete_flows(table=11) + self.br_int.delete_flows(table=12) self.br_int.delete_flows(table=INGRESS_TABLE) self.br_int.install_goto(dest_table_id=INGRESS_TABLE, priority=PC_DEF_PRI, @@ -321,6 +378,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): # and group table buckets = [] vlan = self._get_vlan_by_port(flowrule['egress']) + # A2 Group Creation for item in next_hops: bucket = ( 'bucket=weight=%d, mod_dl_dst:%s,' @@ -331,6 +389,8 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): ) ) buckets.append(bucket) + # A3 In table 5, add MPLS header and send to either patch port + # or table 10 for remote and local node respectively. subnet_actions_list = [] push_mpls = ( "push_mpls:0x8847," @@ -341,6 +401,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): flowrule['nsi'], vlan)) subnet_actions_list.append(push_mpls) + priority = 0 if item['local_endpoint'] == self.local_ip: subnet_actions = ( "resubmit(,%d)" % INGRESS_TABLE) @@ -351,11 +412,37 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): self.br_int.add_flow( table=ACROSS_SUBNET_TABLE, - priority=0, + priority=priority, dl_dst=item['mac_address'], dl_type=0x0800, actions="%s" % ','.join(subnet_actions_list)) + # Same subnet with next hop BUT to prevent hair pinning + if item['local_endpoint'] != self.local_ip: + subnet_actions_list = [] + push_mpls = ( + "strip_vlan," + "push_mpls:0x8847," + "set_mpls_label:%d," + "set_mpls_ttl:%d," + "mod_vlan_vid:%d," % + ((flowrule['nsp'] << 8) | flowrule['nsi'], + flowrule['nsi'], vlan)) + subnet_actions_list.append(push_mpls) + + priority = 10 + subnet_actions = "in_port" + subnet_actions_list.append(subnet_actions) + + self.br_int.add_flow( + table=ACROSS_SUBNET_TABLE, + priority=priority, + in_port=self.patch_tun_ofport, + vlan_tci='0x1000/0x1000', + dl_dst=item['mac_address'], + dl_type=0x0800, + actions="%s" % ','.join(subnet_actions_list)) + buckets = ','.join(buckets) group_content = self.br_int.dump_group_for_id(group_id) if group_content.find('group_id=%d' % group_id) == -1: @@ -365,7 +452,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): self.br_int.mod_group(group_id=group_id, type='select', buckets=buckets) - # 2nd, install br-int flow rule on table 0 for egress traffic + '''# 2nd, install br-int flow rule on table 0 for egress traffic # for egress traffic enc_actions = ("group:%d" % group_id) # to uninstall the removed flow classifiers @@ -376,14 +463,16 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): add_flow=False, match_inport=match_inport) # to install the added flow classifiers + # This is for packets coming out of Src VM self._setup_local_switch_flows_on_int_br( flowrule, flowrule['add_fcs'], enc_actions, add_flow=True, - match_inport=match_inport) + match_inport=match_inport)''' else: - # to uninstall the new removed flow classifiers + pass + '''# to uninstall the new removed flow classifiers self._setup_local_switch_flows_on_int_br( flowrule, flowrule['del_fcs'], @@ -393,12 +482,123 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): ) # to install the added flow classifiers + # This is for packets coming out of SF egress self._setup_local_switch_flows_on_int_br( flowrule, flowrule['add_fcs'], actions='normal', add_flow=True, - match_inport=True) + match_inport=True)''' + self._setup_source_based_flows( + flowrule, + flowrule['add_fcs'], + add_flow=True, + match_inport=True) + + def _update_flows(self, table, priority, + match_info, actions=None, add_flow=True): + if add_flow: + self.br_int.add_flow(table=table, + priority=priority, + actions=actions, + **match_info) + else: + self.br_int.delete_flows(table=table, + priority=priority, + **match_info) + + def _check_if_local_port(self, port_id): + try: + if self.br_int.get_vif_port_by_id(port_id): + return True + except Exception: + pass + return False + + def _setup_source_based_flows( + self, flowrule, flow_classifier_list, + add_flow=True, match_inport=True + ): + inport_match = {} + priority = 50 + vlan_tag = flowrule['segment_id'] + + if match_inport is True: + egress_port = self.br_int.get_vif_port_by_id(flowrule['egress']) + if egress_port: + inport_match = dict(in_port=egress_port.ofport) + + group_id = flowrule.get('next_group_id') + next_hops = flowrule.get('next_hops') + if not (group_id and next_hops): + egress_mac = egress_port.vif_mac + # B6. For packets coming out of SF, we resubmit to table 12. + match_info = dict(dl_type=0x0800, **inport_match) + actions = ("resubmit(,%s)" % 12) + + self._update_flows(ovs_consts.LOCAL_SWITCHING, 60, + match_info, actions, add_flow) + + # B7 In table 12, we decide whether to send it locally or remotely. + for fc in flow_classifier_list: + ldp_port_id = fc['logical_destination_port'] + ldp_mac = fc['ldp_mac_address'] + + if self._check_if_local_port(ldp_port_id): + actions = ("normal") + else: + # TODO(dpak): Uncomment the following once we have a + # vlan enabled setup + '''actions = ("mod_dl_src:%s, output:%s" % ( + egress_mac, self.patch_tun_ofport))''' + actions = ("mod_vlan_vid:1, mod_dl_src:%s, output:%s" % ( + egress_mac, self.patch_tun_ofport)) + + # TODO(dpak): Uncomment the following once we have a + # vlan enabled setup + '''match_info = dict(dl_dst=ldp_mac, + dl_type=0x0800, + dl_vlan=vlan_tag)''' + match_info = dict(dl_dst=ldp_mac, + dl_type=0x0800) + self._update_flows(12, priority, + match_info, actions, add_flow) + return + + # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. + # Action is redirect to group. + ldp_mac = flow_classifier_list[0]['ldp_mac_address'] + actions = ("group:%d" % group_id) + # TODO(dpak): Uncomment the following once we have a vlan enabled setup + '''match_info = dict(inport_match, **{'dl_dst': ldp_mac, + 'dl_type': '0x0800', + 'dl_vlan': vlan_tag})''' + match_info = dict(inport_match, **{'dl_dst': ldp_mac, + 'dl_type': '0x0800'}) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_info, actions, add_flow) + + def _get_port_info(self, port_id, info_type): + ''' Returns specific port info + + @param port_id: Neutron port id + @param info_type: Type is List [mac,ofport,vlan] + @return: Tuple (MAC address, openflow port number) + + ''' + + res = () + port = self.br_int.get_vif_port_by_id(port_id) + + port_type_map = { + 'mac': port.vif_mac, + 'ofport': port.ofport, + 'vlan': self._get_vlan_by_port(port_id)} + + for each in info_type: + res += (port_type_map[each],) + + return res def _get_vlan_by_port(self, port_id): try: @@ -407,8 +607,100 @@ def _get_vlan_by_port(self, port_id): except (vlanmanager.VifIdNotFound, vlanmanager.MappingNotFound): return None + def _setup_destination_based_forwarding(self, flowrule, + flow_classifier_list, + add_flow=True): + priority = 50 + ingress_mac, ingress_ofport = self._get_port_info( + flowrule['ingress'], ['mac', 'ofport']) + vlan_tag = flowrule['segment_id'] + + group_id = flowrule.get('next_group_id') + next_hops = flowrule.get('next_hops') + if not (group_id and next_hops): + # B5. At ingress of SF, modify dest mac to that of Dest VM's + # if nw_dst belongs to Dest VM and output to ingress port of SF. + for fc in flow_classifier_list: + ldp_mac = fc['ldp_mac_address'] + dst_ip = fc['destination_ip_prefix'] + + # TODO(dpak): Uncomment the following once we have a + # vlan enabled setup + '''match_info = dict(dl_type=0x0800, + dl_vlan=vlan_tag, + dl_dst=ingress_mac, + nw_dst=dst_ip)''' + match_info = dict(dl_type=0x0800, + dl_vlan=1, + dl_dst=ingress_mac, + nw_dst=dst_ip) + actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( + ldp_mac, ingress_ofport)) + self._update_flows(11, 60, + match_info, actions, add_flow) + + # B4. At ingress of SF, if dest mac matches with SF ingress, + # strip the mpls packet off mpls and resubmit to 11. + # This is per ldp because ldps can have different vlan tags. + # TODO(dpak): Uncomment the following once we have a + # vlan enabled setup + '''match_info = dict( + dl_type=0x8847, + dl_vlan=vlan_tag, dl_dst=ingress_mac, + mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1))''' + match_info = dict( + dl_type=0x8847, + dl_dst=ingress_mac, + mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1)) + actions = ("pop_mpls:0x0800, resubmit(,11)") + self._update_flows(INGRESS_TABLE, priority, + match_info, actions, add_flow) + return + + # B9. Match IP packets with vlan and source IP. Actions will be to + # strip vlan and modify src MAC and output to Dest VM port. + nw_src = flow_classifier_list[0]['source_ip_prefix'] + + src_port_mac = flow_classifier_list[0]['lsp_mac_address'] + actions = ('strip_vlan, mod_dl_src:%s, output:%s' % ( + src_port_mac, ingress_ofport)) + + # TODO(dpak): Uncomment the following once we have a vlan enabled setup + '''match_field = dict( + dl_type=0x0800, + dl_vlan=vlan_tag, + dl_dst=ingress_mac, + nw_src=nw_src)''' + + match_field = dict( + dl_type=0x0800, + dl_vlan=1, + dl_dst=ingress_mac, + nw_src=nw_src) + + self._update_flows(11, priority, + match_field, actions, add_flow) + + # B8. At ingress of dest, match ip packet with vlan tag and dest + # MAC address. Action will be to resubmit to 11. + # TODO(dpak): Uncomment the following once we have a vlan enabled setup + '''match_field = dict( + dl_type=0x0800, + dl_vlan=vlan_tag, + dl_dst=ingress_mac)''' + match_field = dict( + dl_type=0x0800, + dl_dst=ingress_mac) + actions = ("resubmit(,%s)" % 11) + + self._update_flows(0, priority, match_field, actions, add_flow) + def _setup_ingress_flow_rules_with_mpls(self, flowrule): - vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) + flow_classifier_list = flowrule['add_fcs'] + self._setup_destination_based_forwarding(flowrule, + flow_classifier_list) + + '''vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) if vif_port: vlan = self._get_vlan_by_port(flowrule['ingress']) # install br-int flow rule on table 0 for ingress traffic @@ -425,4 +717,4 @@ def _setup_ingress_flow_rules_with_mpls(self, flowrule): mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1), actions=actions) - self.br_int.add_flow(**match_field) + self.br_int.add_flow(**match_field)''' diff --git a/networking_sfc/services/sfc/drivers/oc/__init__.py b/networking_sfc/services/sfc/drivers/oc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/networking_sfc/services/sfc/drivers/oc/driver.py b/networking_sfc/services/sfc/drivers/oc/driver.py new file mode 100644 index 00000000..e02a81c2 --- /dev/null +++ b/networking_sfc/services/sfc/drivers/oc/driver.py @@ -0,0 +1,42 @@ +from oslo_log import helpers as log_helpers + +from networking_sfc.services.flowclassifier.common import exceptions as exc +from networking_sfc.services.flowclassifier.drivers import base as fc_driver + + +class OVSFlowClassifierDriver(fc_driver.FlowClassifierDriverBase): + """FlowClassifier Driver Base Class.""" + + def initialize(self): + pass + + @log_helpers.log_method_call + def create_flow_classifier(self, context): + pass + + @log_helpers.log_method_call + def update_flow_classifier(self, context): + pass + + @log_helpers.log_method_call + def delete_flow_classifier(self, context): + pass + + @log_helpers.log_method_call + def create_flow_classifier_precommit(self, context): + """OVS Driver precommit before transaction committed. + + Make sure the logical_source_port is not None. + Make sure the logical_destination_port is None. + """ + flow_classifier = context.current + logical_source_port = flow_classifier['logical_source_port'] + if logical_source_port is None: + raise exc.FlowClassifierBadRequest(message=( + 'FlowClassifier %s does not set ' + 'logical source port in ovs driver' % flow_classifier['id'])) + logical_destination_port = flow_classifier['logical_destination_port'] + if logical_destination_port is not None: + raise exc.FlowClassifierBadRequest(message=( + 'FlowClassifier %s sets logical destination port ' + 'in ovs driver' % flow_classifier['id'])) diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index 8f770abf..87af0352 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -261,31 +261,57 @@ def _add_flowclassifier_port_assoc(self, fc_ids, tenant_id, src_node): for fc in self._get_fcs_by_ids(fc_ids): need_assoc = True - # lookup the source port - src_pd_filter = dict( - egress=fc['logical_source_port'], - tenant_id=tenant_id - ) - src_pd = self.get_port_detail_by_filter(src_pd_filter) + src_pd_filter = dst_pd_filter = None - if not src_pd: - # Create source port detail - src_pd = self._create_port_detail(src_pd_filter) - LOG.debug('create src port detail: %s', src_pd) + if fc['logical_source_port']: + # lookup the source port + src_pd_filter = dict( + egress=fc['logical_source_port'], + tenant_id=tenant_id + ) + if fc['logical_destination_port']: + # lookup the source port + dst_pd_filter = dict( + ingress=fc['logical_destination_port'], + tenant_id=tenant_id + ) + src_pd = self.get_port_detail_by_filter(src_pd_filter) + dst_pd = self.get_port_detail_by_filter(dst_pd_filter) + + new_src_pd = new_dst_pd = '' + if not (src_pd and dst_pd): + if not src_pd: + # Create source port detail + new_src_pd = self._create_port_detail(src_pd_filter) + LOG.debug('create src port detail: %s', new_src_pd) + if not dst_pd: + # Create destination port detail + new_dst_pd = self._create_port_detail(dst_pd_filter) + LOG.debug('create dst port detail: %s', new_dst_pd) else: for path_node in src_pd['path_nodes']: if path_node['pathnode_id'] == src_node['id']: need_assoc = False if need_assoc: # Create associate relationship - assco_args = { - 'portpair_id': src_pd['id'], - 'pathnode_id': src_node['id'], - 'weight': 1, - } - sna = self.create_pathport_assoc(assco_args) - LOG.debug('create assoc src port with node: %s', sna) - src_node['portpair_details'].append(src_pd['id']) + if new_src_pd: + assco_args = { + 'portpair_id': new_src_pd['id'], + 'pathnode_id': src_node['id'], + 'weight': 1, + } + sna = self.create_pathport_assoc(assco_args) + LOG.debug('create assoc src port with node: %s', sna) + src_node['portpair_details'].append(new_src_pd['id']) + if new_dst_pd: + assco_args = { + 'portpair_id': new_dst_pd['id'], + 'pathnode_id': src_node['id'], + 'weight': 1, + } + sna = self.create_pathport_assoc(assco_args) + LOG.debug('create assoc src port with node: %s', sna) + src_node['portpair_details'].append(new_dst_pd['id']) def _remove_flowclassifier_port_assoc(self, fc_ids, tenant_id, src_node): @@ -309,9 +335,64 @@ def _remove_flowclassifier_port_assoc(self, fc_ids, tenant_id, if len(pd['path_nodes']) == 1: self.delete_port_detail(pd['id']) + def _create_src_and_dest_nodes(self, port_chain, next_group_intid, + next_group_members, path_nodes): + path_id = port_chain['chain_id'] + port_pair_groups = port_chain['port_pair_groups'] + sf_path_length = len(port_pair_groups) + + # Create a head node object for port chain + src_args = {'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.SRC_NODE, + 'nsp': path_id, + 'nsi': 0xff, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': next_group_intid, + 'next_hop': jsonutils.dumps(next_group_members), + } + src_node = self.create_path_node(src_args) + LOG.debug('create src node: %s', src_node) + path_nodes.append(src_node) + + # Create a destination node object for port chain + dst_args = { + 'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.DST_NODE, + 'nsp': path_id, + 'nsi': 0xff - sf_path_length - 1, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': None, + 'next_hop': None + } + dst_node = self.create_path_node(dst_args) + LOG.debug('create dst node: %s', dst_node) + path_nodes.append(dst_node) + + return src_node + + def _check_if_bi_node(self, src_node): + ''' We assume that if the flow classifiers associated with + a port chain contain both LSP and LDP, we treat it as a bi-directional + chain. LSP and LDP can be part of same or different flow + classifier(s) ''' + + is_lsp = False + is_ldp = False + portpair_details = src_node['portpair_details'] + for each in portpair_details: + port_detail = self.get_port_detail_by_filter(dict(id=each)) + if port_detail['egress']: + is_lsp = True + else: + is_ldp = True + if is_lsp and is_ldp: + return True + return False + @log_helpers.log_method_call def _create_portchain_path(self, context, port_chain): - src_node, src_pd, dst_node, dst_pd = (({}, ) * 4) path_nodes = [] # Create an assoc object for chain_id and path_id # context = context._plugin_context @@ -328,7 +409,11 @@ def _create_portchain_path(self, context, port_chain): # Compare subnets for logical source ports # and first PPG ingress ports for fc in self._get_fcs_by_ids(port_chain['flow_classifiers']): - subnet1 = self._get_subnet_by_port(fc['logical_source_port']) + if fc['logical_source_port']: + subnet1 = self._get_subnet_by_port(fc['logical_source_port']) + else: + subnet1 = self._get_subnet_by_port(fc[ + 'logical_destination_port']) cidr1 = subnet1['cidr'] ppg = context._plugin.get_port_pair_group(context._plugin_context, port_pair_groups[0]) @@ -380,34 +465,9 @@ def _create_portchain_path(self, context, port_chain): next_group_intid, next_group_members = self._get_portgroup_members( context, port_chain['port_pair_groups'][0]) - # Create a head node object for port chain - src_args = {'tenant_id': port_chain['tenant_id'], - 'node_type': ovs_const.SRC_NODE, - 'nsp': path_id, - 'nsi': 0xff, - 'portchain_id': port_chain['id'], - 'status': ovs_const.STATUS_BUILDING, - 'next_group_id': next_group_intid, - 'next_hop': jsonutils.dumps(next_group_members), - } - src_node = self.create_path_node(src_args) - LOG.debug('create src node: %s', src_node) - path_nodes.append(src_node) - - # Create a destination node object for port chain - dst_args = { - 'tenant_id': port_chain['tenant_id'], - 'node_type': ovs_const.DST_NODE, - 'nsp': path_id, - 'nsi': 0xff - sf_path_length - 1, - 'portchain_id': port_chain['id'], - 'status': ovs_const.STATUS_BUILDING, - 'next_group_id': None, - 'next_hop': None - } - dst_node = self.create_path_node(dst_args) - LOG.debug('create dst node: %s', dst_node) - path_nodes.append(dst_node) + src_node = self._create_src_and_dest_nodes( + port_chain, next_group_intid, + next_group_members, path_nodes) self._add_flowclassifier_port_assoc( port_chain['flow_classifiers'], @@ -415,6 +475,11 @@ def _create_portchain_path(self, context, port_chain): src_node ) + is_bi_node = self._check_if_bi_node(src_node) + + if is_bi_node: + path_nodes[0]['node_type'] = 'bi_node' + for i in range(sf_path_length): cur_group_members = next_group_members # next_group for next hop @@ -443,6 +508,8 @@ def _create_portchain_path(self, context, port_chain): } sf_node = self.create_path_node(node_args) LOG.debug('chain path node: %s', sf_node) + + sf_node['node_type'] = 'sf_bi_node' # Create the assocation objects that combine the pathnode_id with # the ingress of the port_pairs in the current group # when port_group does not reach tail @@ -487,10 +554,17 @@ def _delete_portchain_path(self, context, port_chain): pds = self.get_path_nodes_by_filter( dict(portchain_id=port_chain['id'])) src_node = None + is_bi_node = False if pds: for pd in pds: if pd['node_type'] == ovs_const.SRC_NODE: src_node = pd + is_bi_node = self._check_if_bi_node(src_node) + if is_bi_node: + pd['node_type'] = 'bi_node' + # Assuming sf_node comes after src_node. + if is_bi_node and pd['node_type'] == ovs_const.SF_NODE: + pd['node_type'] = 'sf_bi_node' self._delete_path_node_flowrule( pd, port_chain['flow_classifiers'] @@ -519,6 +593,7 @@ def _update_path_node_next_hops(self, flow_rule): dict(id=member['portpair_id'])) if not port_detail or not port_detail['host_id']: continue + detail['local_endpoint'] = port_detail['local_endpoint'] detail['weight'] = member['weight'] detail['mac_address'] = port_detail['mac_address'] @@ -568,6 +643,7 @@ def _filter_flow_classifiers(self, flow_rule, fc_ids): """ fc_return = [] + core_plugin = manager.NeutronManager.get_plugin() if not fc_ids: return fc_return @@ -579,12 +655,28 @@ def _filter_flow_classifiers(self, flow_rule, fc_ids): new_fc.pop('tenant_id') new_fc.pop('description') + lsp = new_fc.get('logical_source_port') + ldp = new_fc.get('logical_destination_port') + if lsp: + port_detail = core_plugin.get_port(self.admin_context, lsp) + new_fc['lsp_mac_address'] = port_detail['mac_address'] + new_fc['source_ip_prefix'] = port_detail[ + 'fixed_ips'][0]['ip_address'] + if ldp: + port_detail = core_plugin.get_port(self.admin_context, ldp) + new_fc['ldp_mac_address'] = port_detail['mac_address'] + new_fc['destination_ip_prefix'] = port_detail[ + 'fixed_ips'][0]['ip_address'] + if ( - flow_rule['node_type'] == ovs_const.SRC_NODE and + flow_rule['node_type'] in [ovs_const.SRC_NODE, 'bi_node'] and flow_rule['egress'] == fc['logical_source_port'] + ) or ( + flow_rule['node_type'] in [ovs_const.SRC_NODE, 'bi_node'] and + flow_rule['ingress'] == fc['logical_destination_port'] ): fc_return.append(new_fc) - elif flow_rule['node_type'] == ovs_const.SF_NODE: + elif flow_rule['node_type'] in [ovs_const.SF_NODE, 'sf_bi_node']: fc_return.append(new_fc) return fc_return @@ -601,6 +693,7 @@ def _update_path_node_port_flowrules(self, node, port, add_fc_ids, del_fc_ids) + LOG.info("YYY FLOW RULES %r" % flow_rule) self.ovs_driver_rpc.ask_agent_to_update_flow_rules( self.admin_context, flow_rule) @@ -649,6 +742,7 @@ def _get_fcs_by_ids(self, fc_ids): def create_port_chain(self, context): port_chain = context.current path_nodes = self._create_portchain_path(context, port_chain) + LOG.info("XXX PATH NODES %r" % path_nodes) self._update_path_nodes( path_nodes, port_chain['flow_classifiers'], From 6b61bfb9e99dd91350908d46f5fcea44c39fcf02 Mon Sep 17 00:00:00 2001 From: dpaks Date: Thu, 13 Apr 2017 16:13:58 +0530 Subject: [PATCH 02/16] 1. Reverted from bi node convention 2. Omitted MPLS 3. Tested with VLAN networking based setup --- .../extensions/openvswitch/sfc_driver.py | 140 ++++++++---------- .../services/sfc/drivers/ovs/driver.py | 74 ++++++--- 2 files changed, 121 insertions(+), 93 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index 5e337ea6..f14be4a6 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -51,6 +51,11 @@ PC_DEF_PRI = 20 PC_INGRESS_PRI = 30 +sfc_ovs_opt = [ + cfg.StrOpt('local_hostname', + default='', help='Hostname of the local machine')] +cfg.CONF.register_opts(sfc_ovs_opt, 'OVS') + class SfcOVSAgentDriver(sfc.SfcAgentDriver): """This class will support MPLS frame @@ -75,6 +80,7 @@ def __init__(self): self.br_int = None self.local_ip = None + self.local_host = None self.patch_tun_ofport = None self.vlan_manager = None @@ -87,8 +93,10 @@ def initialize(self): self.br_int.set_protocols(SfcOVSAgentDriver.REQUIRED_PROTOCOLS) self.local_ip = cfg.CONF.OVS.local_ip - self.patch_tun_ofport = self.br_int.get_port_ofport( - cfg.CONF.OVS.int_peer_patch_port) + self.local_host = cfg.CONF.OVS.local_hostname + '''self.patch_tun_ofport = self.br_int.get_port_ofport( + cfg.CONF.OVS.int_peer_patch_port)''' + self.patch_tun_ofport = self.br_int.get_port_ofport("int-br-vlan") self.vlan_manager = vlanmanager.LocalVlanManager() self._clear_sfc_flow_on_int_br() @@ -189,24 +197,24 @@ def _reverse_fcs(op): for op in ['add_fcs', 'del_fcs']: _reverse_fcs(op) - if node_type == 'bi_node': + if node_type == 'src_node': rev_flowrule['ingress'], rev_flowrule['egress'] = ( rev_flowrule['egress'], rev_flowrule['ingress']) return rev_flowrule def _setup_reverse_ingress_flow_rules(self, flowrule): - node_type = flowrule['node_type'] - if node_type not in ['bi_node', 'sf_bi_node']: + if not flowrule['reverse_path']: return - rev_flowrule = self._reverse_flow_rules(flowrule, node_type) + rev_flowrule = self._reverse_flow_rules(flowrule, + flowrule['node_type']) self._setup_ingress_flow_rules_with_mpls(rev_flowrule) def _setup_reverse_egress_flow_rules(self, flowrule): - node_type = flowrule['node_type'] - if node_type not in ['bi_node', 'sf_bi_node']: + if not flowrule['reverse_path']: return - rev_flowrule = self._reverse_flow_rules(flowrule, node_type) + rev_flowrule = self._reverse_flow_rules(flowrule, + flowrule['node_type']) self._setup_egress_flow_rules(rev_flowrule) def _clear_sfc_flow_on_int_br(self): @@ -369,6 +377,7 @@ def _setup_local_switch_flows_on_int_br( def _setup_egress_flow_rules(self, flowrule, match_inport=True): group_id = flowrule.get('next_group_id', None) next_hops = flowrule.get('next_hops', None) + global_vlan_tag = flowrule['segment_id'] # if the group is not none, install the egress rule for this SF if ( @@ -377,7 +386,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): # 1st, install br-int flow rule on table ACROSS_SUBNET_TABLE # and group table buckets = [] - vlan = self._get_vlan_by_port(flowrule['egress']) + local_vlan = self._get_vlan_by_port(flowrule['egress']) # A2 Group Creation for item in next_hops: bucket = ( @@ -392,22 +401,16 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): # A3 In table 5, add MPLS header and send to either patch port # or table 10 for remote and local node respectively. subnet_actions_list = [] - push_mpls = ( - "push_mpls:0x8847," - "set_mpls_label:%d," - "set_mpls_ttl:%d," - "mod_vlan_vid:%d," % - ((flowrule['nsp'] << 8) | flowrule['nsi'], - flowrule['nsi'], vlan)) - subnet_actions_list.append(push_mpls) - - priority = 0 - if item['local_endpoint'] == self.local_ip: + + priority = 30 + if item['local_endpoint'] == self.local_host: subnet_actions = ( - "resubmit(,%d)" % INGRESS_TABLE) + "mod_vlan_vid:%d, resubmit(,%d)" % (global_vlan_tag, + INGRESS_TABLE)) else: # same subnet with next hop - subnet_actions = "output:%s" % self.patch_tun_ofport + subnet_actions = "mod_vlan_vid:%d, output:%s" % ( + local_vlan, self.patch_tun_ofport) subnet_actions_list.append(subnet_actions) self.br_int.add_flow( @@ -418,18 +421,8 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): actions="%s" % ','.join(subnet_actions_list)) # Same subnet with next hop BUT to prevent hair pinning - if item['local_endpoint'] != self.local_ip: + '''if item['local_endpoint'] != self.local_host: subnet_actions_list = [] - push_mpls = ( - "strip_vlan," - "push_mpls:0x8847," - "set_mpls_label:%d," - "set_mpls_ttl:%d," - "mod_vlan_vid:%d," % - ((flowrule['nsp'] << 8) | flowrule['nsi'], - flowrule['nsi'], vlan)) - subnet_actions_list.append(push_mpls) - priority = 10 subnet_actions = "in_port" subnet_actions_list.append(subnet_actions) @@ -441,7 +434,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): vlan_tci='0x1000/0x1000', dl_dst=item['mac_address'], dl_type=0x0800, - actions="%s" % ','.join(subnet_actions_list)) + actions="%s" % ','.join(subnet_actions_list))''' buckets = ','.join(buckets) group_content = self.br_int.dump_group_for_id(group_id) @@ -521,7 +514,8 @@ def _setup_source_based_flows( ): inport_match = {} priority = 50 - vlan_tag = flowrule['segment_id'] + global_vlan_tag = flowrule['segment_id'] + local_vlan_tag = self._get_vlan_by_port(flowrule['egress']) if match_inport is True: egress_port = self.br_int.get_vif_port_by_id(flowrule['egress']) @@ -534,7 +528,7 @@ def _setup_source_based_flows( egress_mac = egress_port.vif_mac # B6. For packets coming out of SF, we resubmit to table 12. match_info = dict(dl_type=0x0800, **inport_match) - actions = ("resubmit(,%s)" % 12) + actions = ("resubmit(,%s)" % ACROSS_SUBNET_TABLE) self._update_flows(ovs_consts.LOCAL_SWITCHING, 60, match_info, actions, add_flow) @@ -545,34 +539,30 @@ def _setup_source_based_flows( ldp_mac = fc['ldp_mac_address'] if self._check_if_local_port(ldp_port_id): - actions = ("normal") + actions = ("strip_vlan, normal") else: - # TODO(dpak): Uncomment the following once we have a - # vlan enabled setup - '''actions = ("mod_dl_src:%s, output:%s" % ( - egress_mac, self.patch_tun_ofport))''' - actions = ("mod_vlan_vid:1, mod_dl_src:%s, output:%s" % ( - egress_mac, self.patch_tun_ofport)) + actions = ("mod_vlan_vid:%s, mod_dl_src:%s, output:%s" % ( + local_vlan_tag, egress_mac, self.patch_tun_ofport)) - # TODO(dpak): Uncomment the following once we have a - # vlan enabled setup - '''match_info = dict(dl_dst=ldp_mac, - dl_type=0x0800, - dl_vlan=vlan_tag)''' match_info = dict(dl_dst=ldp_mac, - dl_type=0x0800) - self._update_flows(12, priority, + dl_type=0x0800, + dl_vlan=global_vlan_tag) + '''match_info = dict(dl_dst=ldp_mac, + dl_type=0x0800)''' + self._update_flows(ACROSS_SUBNET_TABLE, priority, match_info, actions, add_flow) return # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. # Action is redirect to group. ldp_mac = flow_classifier_list[0]['ldp_mac_address'] - actions = ("group:%d" % group_id) + actions = ("group:%d" % (group_id)) + '''actions = ("mod_vlan_vid:%s, group:%d" % ( + global_vlan_tag, group_id))''' # TODO(dpak): Uncomment the following once we have a vlan enabled setup '''match_info = dict(inport_match, **{'dl_dst': ldp_mac, 'dl_type': '0x0800', - 'dl_vlan': vlan_tag})''' + 'dl_vlan': global_vlan_tag})''' match_info = dict(inport_match, **{'dl_dst': ldp_mac, 'dl_type': '0x0800'}) self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, @@ -613,7 +603,8 @@ def _setup_destination_based_forwarding(self, flowrule, priority = 50 ingress_mac, ingress_ofport = self._get_port_info( flowrule['ingress'], ['mac', 'ofport']) - vlan_tag = flowrule['segment_id'] + local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) + global_vlan_tag = flowrule['segment_id'] group_id = flowrule.get('next_group_id') next_hops = flowrule.get('next_hops') @@ -626,34 +617,32 @@ def _setup_destination_based_forwarding(self, flowrule, # TODO(dpak): Uncomment the following once we have a # vlan enabled setup - '''match_info = dict(dl_type=0x0800, - dl_vlan=vlan_tag, - dl_dst=ingress_mac, - nw_dst=dst_ip)''' match_info = dict(dl_type=0x0800, - dl_vlan=1, + dl_vlan=global_vlan_tag, dl_dst=ingress_mac, nw_dst=dst_ip) - actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( - ldp_mac, ingress_ofport)) - self._update_flows(11, 60, + '''match_info = dict(dl_type=0x0800, + dl_vlan=1, + dl_dst=ingress_mac, + nw_dst=dst_ip)''' + '''actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( + ldp_mac, ingress_ofport))''' + actions = ("mod_dl_dst:%s, output:%s" % ( + ldp_mac, ingress_ofport)) + + self._update_flows(INGRESS_TABLE, 60, match_info, actions, add_flow) # B4. At ingress of SF, if dest mac matches with SF ingress, - # strip the mpls packet off mpls and resubmit to 11. + # vlan matches, then resubmit to 10. # This is per ldp because ldps can have different vlan tags. # TODO(dpak): Uncomment the following once we have a # vlan enabled setup - '''match_info = dict( - dl_type=0x8847, - dl_vlan=vlan_tag, dl_dst=ingress_mac, - mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1))''' match_info = dict( - dl_type=0x8847, - dl_dst=ingress_mac, - mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1)) - actions = ("pop_mpls:0x0800, resubmit(,11)") - self._update_flows(INGRESS_TABLE, priority, + dl_type=0x0800, + dl_vlan=global_vlan_tag, dl_dst=ingress_mac) + actions = ("resubmit(,%s)" % INGRESS_TABLE) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, match_info, actions, add_flow) return @@ -668,13 +657,13 @@ def _setup_destination_based_forwarding(self, flowrule, # TODO(dpak): Uncomment the following once we have a vlan enabled setup '''match_field = dict( dl_type=0x0800, - dl_vlan=vlan_tag, + dl_vlan=local_vlan_tag, dl_dst=ingress_mac, nw_src=nw_src)''' match_field = dict( dl_type=0x0800, - dl_vlan=1, + dl_vlan=global_vlan_tag, dl_dst=ingress_mac, nw_src=nw_src) @@ -686,10 +675,11 @@ def _setup_destination_based_forwarding(self, flowrule, # TODO(dpak): Uncomment the following once we have a vlan enabled setup '''match_field = dict( dl_type=0x0800, - dl_vlan=vlan_tag, + dl_vlan=local_vlan_tag, dl_dst=ingress_mac)''' match_field = dict( dl_type=0x0800, + dl_vlan=global_vlan_tag, dl_dst=ingress_mac) actions = ("resubmit(,%s)" % 11) diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index 87af0352..c955d00c 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -478,7 +478,9 @@ def _create_portchain_path(self, context, port_chain): is_bi_node = self._check_if_bi_node(src_node) if is_bi_node: - path_nodes[0]['node_type'] = 'bi_node' + path_nodes[0].update(dict(reverse_path=True)) + else: + path_nodes[0].update(dict(reverse_path=False)) for i in range(sf_path_length): cur_group_members = next_group_members @@ -509,7 +511,10 @@ def _create_portchain_path(self, context, port_chain): sf_node = self.create_path_node(node_args) LOG.debug('chain path node: %s', sf_node) - sf_node['node_type'] = 'sf_bi_node' + if is_bi_node: + sf_node.update(dict(reverse_path=True)) + else: + sf_node.update(dict(reverse_path=False)) # Create the assocation objects that combine the pathnode_id with # the ingress of the port_pairs in the current group # when port_group does not reach tail @@ -534,6 +539,11 @@ def _delete_path_node_port_flowrule(self, node, port, fc_ids): None, fc_ids) + if flow_rule['reverse_path']: + if (flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule[ + 'ingress']): + flow_rule = self._reverse_flow_rules(flow_rule) + self.ovs_driver_rpc.ask_agent_to_delete_flow_rules( self.admin_context, flow_rule) @@ -557,14 +567,15 @@ def _delete_portchain_path(self, context, port_chain): is_bi_node = False if pds: for pd in pds: + is_bi_node = self._check_if_bi_node(pd) + if is_bi_node: + pd.update(dict(reverse_path=True)) + else: + pd.update(dict(reverse_path=False)) + if pd['node_type'] == ovs_const.SRC_NODE: src_node = pd - is_bi_node = self._check_if_bi_node(src_node) - if is_bi_node: - pd['node_type'] = 'bi_node' - # Assuming sf_node comes after src_node. - if is_bi_node and pd['node_type'] == ovs_const.SF_NODE: - pd['node_type'] = 'sf_bi_node' + self._delete_path_node_flowrule( pd, port_chain['flow_classifiers'] @@ -669,18 +680,37 @@ def _filter_flow_classifiers(self, flow_rule, fc_ids): 'fixed_ips'][0]['ip_address'] if ( - flow_rule['node_type'] in [ovs_const.SRC_NODE, 'bi_node'] and + flow_rule['node_type'] in [ovs_const.SRC_NODE] and flow_rule['egress'] == fc['logical_source_port'] ) or ( - flow_rule['node_type'] in [ovs_const.SRC_NODE, 'bi_node'] and + flow_rule['node_type'] in [ovs_const.SRC_NODE] and flow_rule['ingress'] == fc['logical_destination_port'] ): fc_return.append(new_fc) - elif flow_rule['node_type'] in [ovs_const.SF_NODE, 'sf_bi_node']: + elif flow_rule['node_type'] in [ovs_const.SF_NODE]: fc_return.append(new_fc) return fc_return + def _reverse_flow_rules(self, flowrule): + + def _reverse_fcs(op): + for fc in flowrule[op]: + fc['logical_destination_port'], fc['logical_source_port'] = ( + fc['logical_source_port'], fc['logical_destination_port']) + fc['ldp_mac_address'], fc['lsp_mac_address'] = ( + fc['lsp_mac_address'], fc['ldp_mac_address']) + fc['destination_ip_prefix'], fc['source_ip_prefix'] = ( + fc['source_ip_prefix'], fc['destination_ip_prefix']) + + for op in ['add_fcs', 'del_fcs']: + _reverse_fcs(op) + + flowrule['ingress'], flowrule['egress'] = ( + flowrule['egress'], flowrule['ingress']) + + return flowrule + def _update_path_node_port_flowrules(self, node, port, add_fc_ids=None, del_fc_ids=None): # if this port is not binding, don't to generate flow rule @@ -693,6 +723,11 @@ def _update_path_node_port_flowrules(self, node, port, add_fc_ids, del_fc_ids) + if flow_rule['reverse_path']: + if (flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule[ + 'ingress']): + flow_rule = self._reverse_flow_rules(flow_rule) + LOG.info("YYY FLOW RULES %r" % flow_rule) self.ovs_driver_rpc.ask_agent_to_update_flow_rules( self.admin_context, @@ -891,19 +926,22 @@ def _get_portpair_detail_info(self, portpair_id): network_type = network_info['provider:network_type'] segment_id = network_info['provider:segmentation_id'] - if network_type != np_const.TYPE_VXLAN: - LOG.warning(_LW("Currently only support vxlan network")) + if network_type not in [np_const.TYPE_VXLAN, np_const.TYPE_VLAN]: + LOG.warning(_LW("Currently only support vxlan and vlan networks")) return ((None, ) * 5) elif not host_id: LOG.warning(_LW("This port has not been binding")) return ((None, ) * 5) else: - driver = core_plugin.type_manager.drivers.get(network_type) - host_endpoint = driver.obj.get_endpoint_by_host(host_id) - if host_endpoint: - local_ip = host_endpoint['ip_address'] + if network_type == np_const.TYPE_VXLAN: + driver = core_plugin.type_manager.drivers.get(network_type) + host_endpoint = driver.obj.get_endpoint_by_host(host_id) + if host_endpoint: + local_ip = host_endpoint['ip_address'] + else: + local_ip = None else: - local_ip = None + local_ip = host_id return host_id, local_ip, network_type, segment_id, mac_address From ddc2f29bee8886702f0c341e3e4baa9ee6b080ca Mon Sep 17 00:00:00 2001 From: dpaks Date: Thu, 13 Apr 2017 17:01:43 +0530 Subject: [PATCH 03/16] optimization --- .../services/sfc/agent/extensions/openvswitch/sfc_driver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index f14be4a6..729f32df 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -539,7 +539,9 @@ def _setup_source_based_flows( ldp_mac = fc['ldp_mac_address'] if self._check_if_local_port(ldp_port_id): - actions = ("strip_vlan, normal") + ldp_port = self.br_int.get_vif_port_by_id(ldp_port_id) + ldp_ofport = ldp_port.ofport + actions = ("strip_vlan, output:%s" % ldp_ofport) else: actions = ("mod_vlan_vid:%s, mod_dl_src:%s, output:%s" % ( local_vlan_tag, egress_mac, self.patch_tun_ofport)) From 1c6f0cfb03f182e0c833e7edc3249b8e37e80a45 Mon Sep 17 00:00:00 2001 From: dpaks Date: Mon, 1 May 2017 10:54:35 +0530 Subject: [PATCH 04/16] Using shadow ports for vnf launch --- .../extensions/openvswitch/sfc_driver.py | 4 +- .../services/sfc/drivers/ovs/driver.py | 53 ++++++++++++++++--- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index 729f32df..d75bc97e 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -526,14 +526,14 @@ def _setup_source_based_flows( next_hops = flowrule.get('next_hops') if not (group_id and next_hops): egress_mac = egress_port.vif_mac - # B6. For packets coming out of SF, we resubmit to table 12. + # B6. For packets coming out of SF, we resubmit to table 5. match_info = dict(dl_type=0x0800, **inport_match) actions = ("resubmit(,%s)" % ACROSS_SUBNET_TABLE) self._update_flows(ovs_consts.LOCAL_SWITCHING, 60, match_info, actions, add_flow) - # B7 In table 12, we decide whether to send it locally or remotely. + # B7 In table 5, we decide whether to send it locally or remotely. for fc in flow_classifier_list: ldp_port_id = fc['logical_destination_port'] ldp_mac = fc['ldp_mac_address'] diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index c955d00c..c478abb6 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -408,7 +408,7 @@ def _create_portchain_path(self, context, port_chain): # Detect cross-subnet transit # Compare subnets for logical source ports # and first PPG ingress ports - for fc in self._get_fcs_by_ids(port_chain['flow_classifiers']): + '''for fc in self._get_fcs_by_ids(port_chain['flow_classifiers']): if fc['logical_source_port']: subnet1 = self._get_subnet_by_port(fc['logical_source_port']) else: @@ -429,7 +429,7 @@ def _create_portchain_path(self, context, port_chain): if cidr1 != cidr2: LOG.error(_LE('Cross-subnet chain not supported')) raise exc.SfcDriverError() - return None + return None''' # Compare subnets for PPG egress ports # and next PPG ingress ports @@ -741,7 +741,12 @@ def _update_path_node_flowrules(self, node, return for each in node['portpair_details']: port = self.get_port_detail_by_filter(dict(id=each)) + if port: + if node['node_type'] == ovs_const.SF_NODE: + ingress, egress = self._get_ingress_egress_tap_ports(port) + port.update({'ingress': ingress, + 'egress': egress}) self._update_path_node_port_flowrules( node, port, add_fc_ids, del_fc_ids) @@ -945,8 +950,30 @@ def _get_portpair_detail_info(self, portpair_id): return host_id, local_ip, network_type, segment_id, mac_address + def _get_ingress_egress_tap_ports(self, port_pair): + ingress_shadow_port_id = port_pair.get('ingress') + egress_shadow_port_id = port_pair.get('egress') + + core_plugin = manager.NeutronManager.get_plugin() + in_shadow_pd = core_plugin.get_port(self.admin_context, + ingress_shadow_port_id) + eg_shadow_pd = core_plugin.get_port(self.admin_context, + egress_shadow_port_id) + return in_shadow_pd['device_owner'], eg_shadow_pd['device_owner'] + + def _get_shadow_port_segment_id(self, port): + core_plugin = manager.NeutronManager.get_plugin() + port_detail = core_plugin.get_port(self.admin_context, port) + network_id = port_detail['network_id'] + network_info = core_plugin.get_network( + self.admin_context, network_id) + network_type = network_info['provider:network_type'] + segment_id = network_info['provider:segmentation_id'] + + return network_type, segment_id + @log_helpers.log_method_call - def _create_port_detail(self, port_pair): + def _create_port_detail(self, port_pair, is_sf=False): # since first node may not assign the ingress port, and last node may # not assign the egress port. we use one of the # port as the key to get the SF information. @@ -956,11 +983,21 @@ def _create_port_detail(self, port_pair): elif port_pair.get('egress', None): port = port_pair['egress'] - host_id, local_endpoint, network_type, segment_id, mac_address = ( - self._get_portpair_detail_info(port)) + if not is_sf: + host_id, local_endpoint, network_type, segment_id, mac_address = ( + self._get_portpair_detail_info(port)) + + if is_sf: + ingress, _ = self._get_ingress_egress_tap_ports(port_pair) + host_id, local_endpoint, network_type, segment_id, mac_address = ( + self._get_portpair_detail_info(ingress)) + network_type, segment_id = self._get_shadow_port_segment_id(port) + + ingress, egress = port_pair.get('ingress'), port_pair.get('egress') + port_detail = { - 'ingress': port_pair.get('ingress', None), - 'egress': port_pair.get('egress', None), + 'ingress': ingress, + 'egress': egress, 'tenant_id': port_pair['tenant_id'], 'host_id': host_id, 'segment_id': segment_id, @@ -975,7 +1012,7 @@ def _create_port_detail(self, port_pair): @log_helpers.log_method_call def create_port_pair(self, context): port_pair = context.current - self._create_port_detail(port_pair) + self._create_port_detail(port_pair, is_sf=True) @log_helpers.log_method_call def delete_port_pair(self, context): From 6978dd2210e8064da9050760c50ecc6234cfaf8c Mon Sep 17 00:00:00 2001 From: dpaks Date: Tue, 2 May 2017 19:09:10 +0530 Subject: [PATCH 05/16] working for a pc in non-xtreme setup --- networking_sfc/db/sfc_db.py | 4 ++-- .../extensions/openvswitch/sfc_driver.py | 23 ++++++++++++------- .../services/sfc/drivers/ovs/driver.py | 11 +++++---- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/networking_sfc/db/sfc_db.py b/networking_sfc/db/sfc_db.py index 860c8dd3..04d7848a 100644 --- a/networking_sfc/db/sfc_db.py +++ b/networking_sfc/db/sfc_db.py @@ -239,7 +239,7 @@ def _validate_flow_classifiers(self, context, fc_ids, pc_id=None): if fc_assoc and fc_assoc['portchain_id'] != pc_id: raise ext_fc.FlowClassifierInUse(id=fc.id) - query = self._model_query(context, PortChain) + '''query = self._model_query(context, PortChain) for port_chain_db in query.all(): if port_chain_db['id'] == pc_id: continue @@ -260,7 +260,7 @@ def _validate_flow_classifiers(self, context, fc_ids, pc_id=None): raise ext_sfc.PortChainFlowClassifierInConflict( fc_id=fc['id'], pc_id=port_chain_db['id'], pc_fc_id=pc_fc['id'] - ) + )''' def _setup_chain_group_associations( self, context, port_chain, pg_ids diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index d75bc97e..e81f89d0 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -515,7 +515,7 @@ def _setup_source_based_flows( inport_match = {} priority = 50 global_vlan_tag = flowrule['segment_id'] - local_vlan_tag = self._get_vlan_by_port(flowrule['egress']) + local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) if match_inport is True: egress_port = self.br_int.get_vif_port_by_id(flowrule['egress']) @@ -533,6 +533,8 @@ def _setup_source_based_flows( self._update_flows(ovs_consts.LOCAL_SWITCHING, 60, match_info, actions, add_flow) + ingress_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) + ingress_mac = ingress_port.vif_mac # B7 In table 5, we decide whether to send it locally or remotely. for fc in flow_classifier_list: ldp_port_id = fc['logical_destination_port'] @@ -541,12 +543,15 @@ def _setup_source_based_flows( if self._check_if_local_port(ldp_port_id): ldp_port = self.br_int.get_vif_port_by_id(ldp_port_id) ldp_ofport = ldp_port.ofport - actions = ("strip_vlan, output:%s" % ldp_ofport) + actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( + ldp_mac, ldp_ofport)) else: - actions = ("mod_vlan_vid:%s, mod_dl_src:%s, output:%s" % ( - local_vlan_tag, egress_mac, self.patch_tun_ofport)) + actions = ("mod_vlan_vid:%s, mod_dl_src:%s, " + "mod_dl_dst:%s, output:%s" % ( + local_vlan_tag, egress_mac, ldp_mac, self.patch_tun_ofport)) - match_info = dict(dl_dst=ldp_mac, + match_info = dict(nw_dst=fc['destination_ip_prefix'], + dl_dst=ingress_mac, dl_type=0x0800, dl_vlan=global_vlan_tag) '''match_info = dict(dl_dst=ldp_mac, @@ -629,8 +634,9 @@ def _setup_destination_based_forwarding(self, flowrule, nw_dst=dst_ip)''' '''actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( ldp_mac, ingress_ofport))''' - actions = ("mod_dl_dst:%s, output:%s" % ( - ldp_mac, ingress_ofport)) + '''actions = ("mod_dl_dst:%s, output:%s" % ( + ldp_mac, ingress_ofport))''' + actions = ("strip_vlan, output:%s" % (ingress_ofport)) self._update_flows(INGRESS_TABLE, 60, match_info, actions, add_flow) @@ -685,7 +691,8 @@ def _setup_destination_based_forwarding(self, flowrule, dl_dst=ingress_mac) actions = ("resubmit(,%s)" % 11) - self._update_flows(0, priority, match_field, actions, add_flow) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_field, actions, add_flow) def _setup_ingress_flow_rules_with_mpls(self, flowrule): flow_classifier_list = flowrule['add_fcs'] diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index c478abb6..53a6b86c 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -744,9 +744,8 @@ def _update_path_node_flowrules(self, node, if port: if node['node_type'] == ovs_const.SF_NODE: - ingress, egress = self._get_ingress_egress_tap_ports(port) - port.update({'ingress': ingress, - 'egress': egress}) + _, egress = self._get_ingress_egress_tap_ports(port) + port.update({'egress': egress}) self._update_path_node_port_flowrules( node, port, add_fc_ids, del_fc_ids) @@ -983,7 +982,10 @@ def _create_port_detail(self, port_pair, is_sf=False): elif port_pair.get('egress', None): port = port_pair['egress'] - if not is_sf: + host_id, local_endpoint, network_type, segment_id, mac_address = ( + self._get_portpair_detail_info(port)) + + '''if not is_sf: host_id, local_endpoint, network_type, segment_id, mac_address = ( self._get_portpair_detail_info(port)) @@ -992,6 +994,7 @@ def _create_port_detail(self, port_pair, is_sf=False): host_id, local_endpoint, network_type, segment_id, mac_address = ( self._get_portpair_detail_info(ingress)) network_type, segment_id = self._get_shadow_port_segment_id(port) + ''' ingress, egress = port_pair.get('ingress'), port_pair.get('egress') From 3c3960cc82b246abf00d4676ea202407997ef0d8 Mon Sep 17 00:00:00 2001 From: dpaks Date: Tue, 2 May 2017 19:54:26 +0530 Subject: [PATCH 06/16] tried 2 PCs in diff networks --- .../sfc/agent/extensions/openvswitch/sfc_driver.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index e81f89d0..56be1167 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -409,8 +409,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): INGRESS_TABLE)) else: # same subnet with next hop - subnet_actions = "mod_vlan_vid:%d, output:%s" % ( - local_vlan, self.patch_tun_ofport) + subnet_actions = "output:%s" % self.patch_tun_ofport subnet_actions_list.append(subnet_actions) self.br_int.add_flow( @@ -515,7 +514,7 @@ def _setup_source_based_flows( inport_match = {} priority = 50 global_vlan_tag = flowrule['segment_id'] - local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) + local_vlan_tag = self._get_vlan_by_port(flowrule['egress']) if match_inport is True: egress_port = self.br_int.get_vif_port_by_id(flowrule['egress']) @@ -525,6 +524,7 @@ def _setup_source_based_flows( group_id = flowrule.get('next_group_id') next_hops = flowrule.get('next_hops') if not (group_id and next_hops): + local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) egress_mac = egress_port.vif_mac # B6. For packets coming out of SF, we resubmit to table 5. match_info = dict(dl_type=0x0800, **inport_match) @@ -563,9 +563,9 @@ def _setup_source_based_flows( # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. # Action is redirect to group. ldp_mac = flow_classifier_list[0]['ldp_mac_address'] - actions = ("group:%d" % (group_id)) - '''actions = ("mod_vlan_vid:%s, group:%d" % ( - global_vlan_tag, group_id))''' + '''actions = ("group:%d" % (group_id))''' + actions = ("mod_vlan_vid:%s, group:%d" % ( + local_vlan_tag, group_id)) # TODO(dpak): Uncomment the following once we have a vlan enabled setup '''match_info = dict(inport_match, **{'dl_dst': ldp_mac, 'dl_type': '0x0800', From 7904aa7081350d080e444225c695421810fae45b Mon Sep 17 00:00:00 2001 From: dpaks Date: Wed, 3 May 2017 17:17:33 +0530 Subject: [PATCH 07/16] fixed bug in delete path;still ONE stale flow entry --- .../extensions/openvswitch/sfc_driver.py | 118 ++---------------- .../services/sfc/drivers/ovs/driver.py | 10 +- 2 files changed, 17 insertions(+), 111 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index 56be1167..fb0ce689 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -164,9 +164,8 @@ def delete_flow_rule(self, flowrule, flowrule_status): dl_dst=vif_port.vif_mac, mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1) ) - if node_type in ['bi_node', 'sf_bi_node']: - rev_flowrule = self._reverse_flow_rules( - flowrule, flowrule['node_type']) + if flowrule.get('reverse_path'): + rev_flowrule = self._reverse_flow_rules(flowrule, node_type) if flowrule['ingress'] is not None: self._setup_source_based_flows( rev_flowrule, rev_flowrule['del_fcs'], add_flow=False) @@ -220,8 +219,6 @@ def _setup_reverse_egress_flow_rules(self, flowrule): def _clear_sfc_flow_on_int_br(self): self.br_int.delete_group(group_id='all') self.br_int.delete_flows(table=ACROSS_SUBNET_TABLE) - self.br_int.delete_flows(table=11) - self.br_int.delete_flows(table=12) self.br_int.delete_flows(table=INGRESS_TABLE) self.br_int.install_goto(dest_table_id=INGRESS_TABLE, priority=PC_DEF_PRI, @@ -419,22 +416,6 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): dl_type=0x0800, actions="%s" % ','.join(subnet_actions_list)) - # Same subnet with next hop BUT to prevent hair pinning - '''if item['local_endpoint'] != self.local_host: - subnet_actions_list = [] - priority = 10 - subnet_actions = "in_port" - subnet_actions_list.append(subnet_actions) - - self.br_int.add_flow( - table=ACROSS_SUBNET_TABLE, - priority=priority, - in_port=self.patch_tun_ofport, - vlan_tci='0x1000/0x1000', - dl_dst=item['mac_address'], - dl_type=0x0800, - actions="%s" % ','.join(subnet_actions_list))''' - buckets = ','.join(buckets) group_content = self.br_int.dump_group_for_id(group_id) if group_content.find('group_id=%d' % group_id) == -1: @@ -443,44 +424,9 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): else: self.br_int.mod_group(group_id=group_id, type='select', buckets=buckets) - - '''# 2nd, install br-int flow rule on table 0 for egress traffic - # for egress traffic - enc_actions = ("group:%d" % group_id) - # to uninstall the removed flow classifiers - self._setup_local_switch_flows_on_int_br( - flowrule, - flowrule['del_fcs'], - None, - add_flow=False, - match_inport=match_inport) - # to install the added flow classifiers - # This is for packets coming out of Src VM - self._setup_local_switch_flows_on_int_br( - flowrule, - flowrule['add_fcs'], - enc_actions, - add_flow=True, - match_inport=match_inport)''' else: pass - '''# to uninstall the new removed flow classifiers - self._setup_local_switch_flows_on_int_br( - flowrule, - flowrule['del_fcs'], - None, - add_flow=False, - match_inport=True - ) - # to install the added flow classifiers - # This is for packets coming out of SF egress - self._setup_local_switch_flows_on_int_br( - flowrule, - flowrule['add_fcs'], - actions='normal', - add_flow=True, - match_inport=True)''' self._setup_source_based_flows( flowrule, flowrule['add_fcs'], @@ -548,14 +494,14 @@ def _setup_source_based_flows( else: actions = ("mod_vlan_vid:%s, mod_dl_src:%s, " "mod_dl_dst:%s, output:%s" % ( - local_vlan_tag, egress_mac, ldp_mac, self.patch_tun_ofport)) + (local_vlan_tag, egress_mac, + ldp_mac, self.patch_tun_ofport))) match_info = dict(nw_dst=fc['destination_ip_prefix'], dl_dst=ingress_mac, dl_type=0x0800, dl_vlan=global_vlan_tag) - '''match_info = dict(dl_dst=ldp_mac, - dl_type=0x0800)''' + self._update_flows(ACROSS_SUBNET_TABLE, priority, match_info, actions, add_flow) return @@ -563,13 +509,8 @@ def _setup_source_based_flows( # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. # Action is redirect to group. ldp_mac = flow_classifier_list[0]['ldp_mac_address'] - '''actions = ("group:%d" % (group_id))''' actions = ("mod_vlan_vid:%s, group:%d" % ( local_vlan_tag, group_id)) - # TODO(dpak): Uncomment the following once we have a vlan enabled setup - '''match_info = dict(inport_match, **{'dl_dst': ldp_mac, - 'dl_type': '0x0800', - 'dl_vlan': global_vlan_tag})''' match_info = dict(inport_match, **{'dl_dst': ldp_mac, 'dl_type': '0x0800'}) self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, @@ -622,20 +563,10 @@ def _setup_destination_based_forwarding(self, flowrule, ldp_mac = fc['ldp_mac_address'] dst_ip = fc['destination_ip_prefix'] - # TODO(dpak): Uncomment the following once we have a - # vlan enabled setup match_info = dict(dl_type=0x0800, dl_vlan=global_vlan_tag, dl_dst=ingress_mac, nw_dst=dst_ip) - '''match_info = dict(dl_type=0x0800, - dl_vlan=1, - dl_dst=ingress_mac, - nw_dst=dst_ip)''' - '''actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( - ldp_mac, ingress_ofport))''' - '''actions = ("mod_dl_dst:%s, output:%s" % ( - ldp_mac, ingress_ofport))''' actions = ("strip_vlan, output:%s" % (ingress_ofport)) self._update_flows(INGRESS_TABLE, 60, @@ -644,8 +575,6 @@ def _setup_destination_based_forwarding(self, flowrule, # B4. At ingress of SF, if dest mac matches with SF ingress, # vlan matches, then resubmit to 10. # This is per ldp because ldps can have different vlan tags. - # TODO(dpak): Uncomment the following once we have a - # vlan enabled setup match_info = dict( dl_type=0x0800, dl_vlan=global_vlan_tag, dl_dst=ingress_mac) @@ -662,34 +591,22 @@ def _setup_destination_based_forwarding(self, flowrule, actions = ('strip_vlan, mod_dl_src:%s, output:%s' % ( src_port_mac, ingress_ofport)) - # TODO(dpak): Uncomment the following once we have a vlan enabled setup - '''match_field = dict( - dl_type=0x0800, - dl_vlan=local_vlan_tag, - dl_dst=ingress_mac, - nw_src=nw_src)''' - match_field = dict( dl_type=0x0800, dl_vlan=global_vlan_tag, dl_dst=ingress_mac, nw_src=nw_src) - self._update_flows(11, priority, + self._update_flows(10, priority, match_field, actions, add_flow) # B8. At ingress of dest, match ip packet with vlan tag and dest - # MAC address. Action will be to resubmit to 11. - # TODO(dpak): Uncomment the following once we have a vlan enabled setup - '''match_field = dict( - dl_type=0x0800, - dl_vlan=local_vlan_tag, - dl_dst=ingress_mac)''' + # MAC address. Action will be to resubmit to 10. match_field = dict( dl_type=0x0800, dl_vlan=global_vlan_tag, dl_dst=ingress_mac) - actions = ("resubmit(,%s)" % 11) + actions = ("resubmit(,%s)" % 10) self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, match_field, actions, add_flow) @@ -698,22 +615,3 @@ def _setup_ingress_flow_rules_with_mpls(self, flowrule): flow_classifier_list = flowrule['add_fcs'] self._setup_destination_based_forwarding(flowrule, flow_classifier_list) - - '''vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) - if vif_port: - vlan = self._get_vlan_by_port(flowrule['ingress']) - # install br-int flow rule on table 0 for ingress traffic - match_field = {} - - actions = ("strip_vlan, pop_mpls:0x0800," - "output:%s" % vif_port.ofport) - match_field = dict( - table=INGRESS_TABLE, - priority=1, - dl_dst=vif_port.vif_mac, - dl_vlan=vlan, - dl_type=0x8847, - mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1), - actions=actions) - - self.br_int.add_flow(**match_field)''' diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index 53a6b86c..c914fd51 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -511,6 +511,7 @@ def _create_portchain_path(self, context, port_chain): sf_node = self.create_path_node(node_args) LOG.debug('chain path node: %s', sf_node) + # If Src Node is bi, then SF node shall naturally be bi. if is_bi_node: sf_node.update(dict(reverse_path=True)) else: @@ -544,6 +545,7 @@ def _delete_path_node_port_flowrule(self, node, port, fc_ids): 'ingress']): flow_rule = self._reverse_flow_rules(flow_rule) + LOG.info("ZZZ FLOW RULES %r" % flow_rule) self.ovs_driver_rpc.ask_agent_to_delete_flow_rules( self.admin_context, flow_rule) @@ -556,6 +558,9 @@ def _delete_path_node_flowrule(self, node, fc_ids): for each in node['portpair_details']: port = self.get_port_detail_by_filter(dict(id=each)) if port: + if node['node_type'] == ovs_const.SF_NODE: + _, egress = self._get_ingress_egress_tap_ports(port) + port.update({'egress': egress}) self._delete_path_node_port_flowrule( node, port, fc_ids) @@ -567,7 +572,10 @@ def _delete_portchain_path(self, context, port_chain): is_bi_node = False if pds: for pd in pds: - is_bi_node = self._check_if_bi_node(pd) + # If Src Node is bi, then SF node shall naturally be bi. Here, + # we assume that Src node will be the first in 'pds'. + if self._check_if_bi_node(pd): + is_bi_node = True if is_bi_node: pd.update(dict(reverse_path=True)) else: From 4975f6b12067f3cfc2d577a0b5d53d09a0ad5096 Mon Sep 17 00:00:00 2001 From: dpaks Date: Thu, 4 May 2017 11:08:55 +0530 Subject: [PATCH 08/16] 1. Removed all stale flows after deletion 2. For vxlan networks, get segment id from network segments table --- .../extensions/openvswitch/sfc_driver.py | 12 ++++++---- .../services/sfc/drivers/ovs/driver.py | 23 +++++++++++++++---- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index fb0ce689..d0ce1a54 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -166,10 +166,12 @@ def delete_flow_rule(self, flowrule, flowrule_status): ) if flowrule.get('reverse_path'): rev_flowrule = self._reverse_flow_rules(flowrule, node_type) - if flowrule['ingress'] is not None: + if (flowrule['ingress'] is not None or ( + node_type == ovs_consts.SF_NODE)): self._setup_source_based_flows( rev_flowrule, rev_flowrule['del_fcs'], add_flow=False) - else: + if (flowrule['egress'] is not None or ( + node_type == ovs_consts.SF_NODE)): self._setup_destination_based_forwarding( rev_flowrule, rev_flowrule['del_fcs'], add_flow=False) @@ -196,7 +198,7 @@ def _reverse_fcs(op): for op in ['add_fcs', 'del_fcs']: _reverse_fcs(op) - if node_type == 'src_node': + if node_type == ovs_consts.SRC_NODE: rev_flowrule['ingress'], rev_flowrule['egress'] = ( rev_flowrule['egress'], rev_flowrule['ingress']) @@ -557,8 +559,8 @@ def _setup_destination_based_forwarding(self, flowrule, group_id = flowrule.get('next_group_id') next_hops = flowrule.get('next_hops') if not (group_id and next_hops): - # B5. At ingress of SF, modify dest mac to that of Dest VM's - # if nw_dst belongs to Dest VM and output to ingress port of SF. + # B5. At ingress of SF, if dl_dst belongs to SF and nw_dst + # belongs to Dest VM, output to ingress port of SF. for fc in flow_classifier_list: ldp_mac = fc['ldp_mac_address'] dst_ip = fc['destination_ip_prefix'] diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index c914fd51..74d82ba3 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -22,6 +22,7 @@ from neutron.common import rpc as n_rpc from neutron import context as n_context from neutron.db import api as db_api +from neutron.db import segments_db as seg_db from neutron import manager from neutron.plugins.common import constants as np_const @@ -569,13 +570,14 @@ def _delete_portchain_path(self, context, port_chain): pds = self.get_path_nodes_by_filter( dict(portchain_id=port_chain['id'])) src_node = None - is_bi_node = False + is_bi_node = True if pds: for pd in pds: + ''' We cannot assume that Src node always comes first. # If Src Node is bi, then SF node shall naturally be bi. Here, # we assume that Src node will be the first in 'pds'. if self._check_if_bi_node(pd): - is_bi_node = True + is_bi_node = True''' if is_bi_node: pd.update(dict(reverse_path=True)) else: @@ -955,7 +957,8 @@ def _get_portpair_detail_info(self, portpair_id): else: local_ip = host_id - return host_id, local_ip, network_type, segment_id, mac_address + return (host_id, local_ip, network_type, + segment_id, mac_address, network_id) def _get_ingress_egress_tap_ports(self, port_pair): ingress_shadow_port_id = port_pair.get('ingress') @@ -979,6 +982,14 @@ def _get_shadow_port_segment_id(self, port): return network_type, segment_id + def _get_segmentation_id(self, network_id): + session = db_api.get_session() + nw_seg_data = seg_db.get_network_segments(session, network_id) + + for each in nw_seg_data: + if each['network_type'] == np_const.TYPE_VLAN: + return each['segmentation_id'] + @log_helpers.log_method_call def _create_port_detail(self, port_pair, is_sf=False): # since first node may not assign the ingress port, and last node may @@ -990,9 +1001,13 @@ def _create_port_detail(self, port_pair, is_sf=False): elif port_pair.get('egress', None): port = port_pair['egress'] - host_id, local_endpoint, network_type, segment_id, mac_address = ( + (host_id, local_endpoint, network_type, + segment_id, mac_address, network_id) = ( self._get_portpair_detail_info(port)) + if network_type == np_const.TYPE_VXLAN: + segment_id = self._get_segmentation_id(network_id) + '''if not is_sf: host_id, local_endpoint, network_type, segment_id, mac_address = ( self._get_portpair_detail_info(port)) From 48854708c8278167842ad31e7cfc653f90af6e4a Mon Sep 17 00:00:00 2001 From: dpaks Date: Tue, 9 May 2017 21:30:36 +0530 Subject: [PATCH 09/16] reorg --- demo.sh | 148 +++++ .../flowclassifier/drivers/oc/driver.py | 48 +- .../flowclassifier/drivers/ovs/driver.py | 15 +- .../sfc/agent/extensions/oc/__init__.py | 0 .../sfc/agent/extensions/oc/sfc_driver.py | 448 ++++++++++++++ .../extensions/openvswitch/sfc_driver.py | 323 ++-------- .../services/sfc/drivers/oc/driver.py | 576 +++++++++++++++++- .../services/sfc/drivers/ovs/driver.py | 325 ++-------- setup.cfg | 5 +- 9 files changed, 1312 insertions(+), 576 deletions(-) create mode 100644 demo.sh create mode 100644 networking_sfc/services/sfc/agent/extensions/oc/__init__.py create mode 100644 networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py diff --git a/demo.sh b/demo.sh new file mode 100644 index 00000000..c3243af1 --- /dev/null +++ b/demo.sh @@ -0,0 +1,148 @@ +#!/bin/sh + +########################## FILL THIS INFO ##################################### +# Note: Fill with hostnames. Blank, if compute does not exist. +computeA=compute-168 +computeB=compute-204 +computeC=compute-117 + +# Note: Fill with SF and client glance image names. +sf_image=sf +client_image=client +############################################################################### + + + +if [[ -z $computeA && -z $computeB && -z $computeC ]]; then + echo "Fill the compute node details." + exit +fi + +source ~/devstack/openrc neutron service + +sf_id=`glance image-list | grep " $sf_image " | awk '{print $2}'` +client_id=`glance image-list | grep " $client_image " | awk '{print $2}'` +if [[ -z $sf_id || -z $client_id ]]; then + echo "Fill in valid glance image names for both SF and Client VMs." + exit +fi + +operation=$1 +if [ "$operation" != "create" ] && [ "$operation" != "delete" ]; then + echo "This script expects a case-sensitive argument --> create/delete" + exit +fi + +if [ ! -z $computeC ] ; then + c2_compute=$computeC + c1_compute=$computeA + sf_compute=$computeB +elif [ ! -z $computeB ] ; then + sf_compute=$computeB + c2_compute=$computeA + c1_compute=$computeA +else + sf_compute=$computeA + c2_compute=$computeA + c1_compute=$computeA +fi + +if [ "$operation" == "create" ]; then + openstack network create inspected_net + openstack subnet create --subnet-range 11.0.0.0/24 --network inspected_net inspected_subnet + openstack network create inspection_net + openstack subnet create --subnet-range 12.0.0.0/24 --network inspection_net inspection_subnet + openstack router create sfc-router + + openstack router add subnet sfc-router inspected_subnet + openstack router add subnet sfc-router inspection_subnet + + openstack port create --network inspected_net c1 + openstack port create --network inspected_net c2 + openstack port create --network inspection_net p1 + openstack port create --network inspection_net p2 + + p1_id=`openstack port list -f value | grep " p1 " | awk '{print $1}'` + p2_id=`openstack port list -f value | grep " p2 " | awk '{print $1}'` + p1_mac=`openstack port list -f value | grep " p1 " | awk '{print $3}'` + p2_mac=`openstack port list -f value | grep " p2 " | awk '{print $3}'` + + c1_id=`openstack port list -f value | grep " c1 " | awk '{print $1}'` + c2_id=`openstack port list -f value | grep " c2 " | awk '{print $1}'` + + openstack network trunk create --parent-port $p1_id trunk1 + + echo "Launching VNF on Node $sf_compute..." + openstack server delete VNF + nova boot --image $sf_image --flavor 3 --nic port-id=$p1_id\ + --nic port-id=$p2_id --availability-zone nova:$sf_compute VNF + + sleep 5 + + echo "Launching Attacker on Node $c1_compute..." + openstack server delete Attacker + nova boot --image $client_image --flavor 2 --nic port-id=$c1_id --availability-zone nova:$c1_compute Attacker + echo "Launching InspectedVM on Node $c2_compute..." + openstack server delete InspectedVM + nova boot --image $client_image --flavor 2 --nic port-id=$c2_id --availability-zone nova:$c2_compute InspectedVM + + sleep 5 + + vnf_id=`openstack server list | grep VNF | awk '{print $2}'` + + openstack port create --network inspected_net --mac-address $p1_mac s1 + s1_id=`openstack port list -f value | grep " s1 " | awk '{print $1}'` + seg_id=`openstack network show inspected_net | grep segmentation_id | awk '{print $4}'` + + openstack port create --network inspected_net --device $vnf_id\ + --device-owner $p2_id --mac-address $p2_mac s2 + + openstack network trunk set --subport port=$s1_id,segmentation-type=vlan,segmentation-id=$seg_id trunk1 + + openstack port set --device $vnf_id s1 + + sleep 3 + + openstack port pair create --ingress s1 --egress s2 pp1 + openstack port pair group create --port-pair pp1 ppg1 + openstack flow classifier create --protocol TCP --logical-source-port c1 --logical-destination-port c2 fc1 + + while true; do + read -p "Create or delete the port chain? (create/delete/exit) " op + case $op in + create ) openstack port chain create --port-pair-group ppg1 --flow-classifier fc1 pc1;; + delete ) openstack port chain delete pc1;; + exit ) exit;; + * ) echo "Please choose either of (create/delete/exit).";; + esac + done + +else + openstack port chain delete pc1 + openstack flow classifier delete fc1 + openstack port pair group delete ppg1 + openstack port pair delete pp1 + + openstack server delete Attacker InspectedVM VNF + + s1_id=`openstack port list -f value | grep " s1 " | awk '{print $1}'` + openstack network trunk unset --subport $s1_id trunk1 + openstack network trunk delete trunk1 + + openstack port delete s1 s2 c1 c2 p1 p2 + + openstack router remove subnet sfc-router inspected_subnet + openstack router remove subnet sfc-router inspection_subnet + openstack router delete sfc-router + openstack network delete inspected_net inspection_net +fi + + + + + + + + + + \ No newline at end of file diff --git a/networking_sfc/services/flowclassifier/drivers/oc/driver.py b/networking_sfc/services/flowclassifier/drivers/oc/driver.py index e02a81c2..eb48cf25 100644 --- a/networking_sfc/services/flowclassifier/drivers/oc/driver.py +++ b/networking_sfc/services/flowclassifier/drivers/oc/driver.py @@ -1,42 +1,38 @@ +# Copyright 2016 Futurewei. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + from oslo_log import helpers as log_helpers from networking_sfc.services.flowclassifier.common import exceptions as exc -from networking_sfc.services.flowclassifier.drivers import base as fc_driver +from networking_sfc.services.flowclassifier.drivers.ovs import driver as fc_dvr -class OVSFlowClassifierDriver(fc_driver.FlowClassifierDriverBase): +class OCFlowClassifierDriver(fc_dvr.OVSFlowClassifierDriver): """FlowClassifier Driver Base Class.""" - def initialize(self): - pass - - @log_helpers.log_method_call - def create_flow_classifier(self, context): - pass - - @log_helpers.log_method_call - def update_flow_classifier(self, context): - pass - - @log_helpers.log_method_call - def delete_flow_classifier(self, context): - pass - @log_helpers.log_method_call def create_flow_classifier_precommit(self, context): """OVS Driver precommit before transaction committed. - Make sure the logical_source_port is not None. - Make sure the logical_destination_port is None. + Make sure that either the logical_source_port + or the logical_destination_port is not None. """ + flow_classifier = context.current logical_source_port = flow_classifier['logical_source_port'] - if logical_source_port is None: - raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s does not set ' - 'logical source port in ovs driver' % flow_classifier['id'])) logical_destination_port = flow_classifier['logical_destination_port'] - if logical_destination_port is not None: + if (logical_source_port or logical_destination_port) is None: raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s sets logical destination port ' - 'in ovs driver' % flow_classifier['id'])) + 'FlowClassifier %s requires either logical destination port or' + ' logical source port in ovs driver' % flow_classifier['id'])) diff --git a/networking_sfc/services/flowclassifier/drivers/ovs/driver.py b/networking_sfc/services/flowclassifier/drivers/ovs/driver.py index 8182f49e..4a1bf9ac 100644 --- a/networking_sfc/services/flowclassifier/drivers/ovs/driver.py +++ b/networking_sfc/services/flowclassifier/drivers/ovs/driver.py @@ -40,14 +40,17 @@ def delete_flow_classifier(self, context): def create_flow_classifier_precommit(self, context): """OVS Driver precommit before transaction committed. - Make sure that either the logical_source_port - or the logical_destination_port is not None. + Make sure the logical_source_port is not None. + Make sure the logical_destination_port is None. """ - flow_classifier = context.current logical_source_port = flow_classifier['logical_source_port'] + if logical_source_port is None: + raise exc.FlowClassifierBadRequest(message=( + 'FlowClassifier %s does not set ' + 'logical source port in ovs driver' % flow_classifier['id'])) logical_destination_port = flow_classifier['logical_destination_port'] - if (logical_source_port or logical_destination_port) is None: + if logical_destination_port is not None: raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s requires either logical destination port or' - ' logical source port in ovs driver' % flow_classifier['id'])) + 'FlowClassifier %s sets logical destination port ' + 'in ovs driver' % flow_classifier['id'])) diff --git a/networking_sfc/services/sfc/agent/extensions/oc/__init__.py b/networking_sfc/services/sfc/agent/extensions/oc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py new file mode 100644 index 00000000..efb9dcbc --- /dev/null +++ b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py @@ -0,0 +1,448 @@ +# Copyright 2015 Huawei. +# Copyright 2016 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy + +from neutron_lib import constants as n_consts +from oslo_config import cfg +from oslo_log import log as logging + +from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ + as ovs_consts +from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager + +from networking_sfc._i18n import _LE +from networking_sfc.services.sfc.agent.extensions import sfc +from networking_sfc.services.sfc.common import ovs_ext_lib +from networking_sfc.services.sfc.agent.extensions.openvswitch import sfc_driver +from networking_sfc.services.sfc.drivers.ovs import constants + +LOG = logging.getLogger(__name__) + +cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.' + 'common.config') + +# This table is used to process the traffic across differet subnet scenario. +# Flow 1: pri=1, ip,dl_dst=nexthop_mac,nw_src=nexthop_subnet. actions= +# push_mpls:0x8847,set_mpls_label,set_mpls_ttl,push_vlan,output:(patch port +# or resubmit to table(INGRESS_TABLE) +# Flow 2: pri=0, ip,dl_dst=nexthop_mac,, action=push_mpls:0x8847, +# set_mpls_label,set_mpls_ttl,push_vlan,output:(patch port or resubmit to +# table(INGRESS_TABLE) +ACROSS_SUBNET_TABLE = 5 + +# The table has multiple flows that steer traffic for the different chains +# to the ingress port of different service functions hosted on this Compute +# node. +INGRESS_TABLE = 10 + +# port chain default flow rule priority +PC_DEF_PRI = 20 +PC_INGRESS_PRI = 30 + +sfc_ovs_opt = [ + cfg.StrOpt('local_hostname', + default='', help='Hostname of the local machine'), + cfg.StrOpt('phy_patch_ofport', + default='', help='Patch port of integration bridge ' + 'to tun/vlan bridge.')] +cfg.CONF.register_opts(sfc_ovs_opt, 'OVS') + + +class SfcOCAgentDriver(sfc_driver.SfcOVSAgentDriver): + """This class will support MPLS frame + + Ethernet + MPLS + IPv4 Packet: + +-------------------------------+---------------+--------------------+ + |Outer Ethernet, ET=0x8847 | MPLS head, | original IP Packet | + +-------------------------------+---------------+--------------------+ + """ + + REQUIRED_PROTOCOLS = [ + ovs_consts.OPENFLOW10, + ovs_consts.OPENFLOW11, + ovs_consts.OPENFLOW12, + ovs_consts.OPENFLOW13, + ] + + def __init__(self): + super(SfcOCAgentDriver, self).__init__() + self.ovs_sfc_dvr = sfc_driver.SfcOVSAgentDriver() + self.local_host = None + + def consume_api(self, agent_api): + self.agent_api = agent_api + + def initialize(self): + self.br_int = ovs_ext_lib.SfcOVSBridgeExt( + self.agent_api.request_int_br()) + self.br_int.set_protocols(SfcOCAgentDriver.REQUIRED_PROTOCOLS) + + self.local_ip = cfg.CONF.OVS.local_ip + self.vlan_manager = vlanmanager.LocalVlanManager() + + self._clear_sfc_flow_on_int_br() + self.phy_patch_ofport = self.br_int.get_port_ofport( + cfg.CONF.OVS.phy_patch_ofport) + + def update_flow_rules(self, flowrule, flowrule_status): + try: + if flowrule.get('egress'): + self._setup_egress_flow_rules(flowrule) + self._setup_reverse_ingress_flow_rules(flowrule) + if flowrule.get('ingress'): + self._setup_ingress_flow_rules(flowrule) + self._setup_reverse_egress_flow_rules(flowrule) + + flowrule_status_temp = {} + flowrule_status_temp['id'] = flowrule['id'] + flowrule_status_temp['status'] = constants.STATUS_ACTIVE + flowrule_status.append(flowrule_status_temp) + except Exception as e: + flowrule_status_temp = {} + flowrule_status_temp['id'] = flowrule['id'] + flowrule_status_temp['status'] = constants.STATUS_ERROR + flowrule_status.append(flowrule_status_temp) + LOG.exception(e) + LOG.error(_LE("update_flow_rules failed")) + + def delete_flow_rule(self, flowrule, flowrule_status): + try: + LOG.debug("delete_flow_rule, flowrule = %s", + flowrule) + + node_type = flowrule['node_type'] + # delete tunnel table flow rule on br-int(egress match) + if flowrule['egress'] is not None: + self._setup_source_based_flows( + flowrule, flowrule['del_fcs'], add_flow=False) + # delete group table, need to check again + group_id = flowrule.get('next_group_id', None) + if group_id and flowrule.get('group_refcnt', None) <= 1: + self.br_int.delete_group(group_id=group_id) + for item in flowrule['next_hops']: + self.br_int.delete_flows( + table=ACROSS_SUBNET_TABLE, + dl_dst=item['mac_address']) + + if flowrule['ingress'] is not None: + self._setup_destination_based_flows(flowrule, + flowrule['del_fcs'], + add_flow=False) + # delete table INGRESS_TABLE ingress match flow rule + # on br-int(ingress match) + vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) + if vif_port: + # third, install br-int flow rule on table INGRESS_TABLE + # for ingress traffic + self.br_int.delete_flows( + table=INGRESS_TABLE, + dl_type=0x8847, + dl_dst=vif_port.vif_mac, + mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1) + ) + if flowrule.get('reverse_path'): + rev_flowrule = self._reverse_flow_rules(flowrule, node_type) + if (flowrule['ingress'] is not None or ( + node_type == 'sf_node')): + self._setup_source_based_flows( + rev_flowrule, rev_flowrule['del_fcs'], add_flow=False) + if (flowrule['egress'] is not None or ( + node_type == 'sf_node')): + self._setup_destination_based_flows( + rev_flowrule, rev_flowrule['del_fcs'], + add_flow=False) + except Exception as e: + flowrule_status_temp = {} + flowrule_status_temp['id'] = flowrule['id'] + flowrule_status_temp['status'] = constants.STATUS_ERROR + flowrule_status.append(flowrule_status_temp) + LOG.exception(e) + LOG.error(_LE("delete_flow_rule failed")) + + def _reverse_flow_rules(self, flowrule, node_type): + rev_flowrule = copy.deepcopy(flowrule) + + def _reverse_fcs(op): + for fc in rev_flowrule[op]: + fc['logical_destination_port'], fc['logical_source_port'] = ( + fc['logical_source_port'], fc['logical_destination_port']) + fc['ldp_mac_address'], fc['lsp_mac_address'] = ( + fc['lsp_mac_address'], fc['ldp_mac_address']) + fc['destination_ip_prefix'], fc['source_ip_prefix'] = ( + fc['source_ip_prefix'], fc['destination_ip_prefix']) + + for op in ['add_fcs', 'del_fcs']: + _reverse_fcs(op) + + if node_type == 'src_node': + rev_flowrule['ingress'], rev_flowrule['egress'] = ( + rev_flowrule['egress'], rev_flowrule['ingress']) + + return rev_flowrule + + def _setup_reverse_ingress_flow_rules(self, flowrule): + if not flowrule['reverse_path']: + return + rev_flowrule = self._reverse_flow_rules(flowrule, + flowrule['node_type']) + self._setup_ingress_flow_rules(rev_flowrule) + + def _setup_reverse_egress_flow_rules(self, flowrule): + if not flowrule['reverse_path']: + return + rev_flowrule = self._reverse_flow_rules(flowrule, + flowrule['node_type']) + self._setup_egress_flow_rules(rev_flowrule) + + def _setup_egress_flow_rules(self, flowrule, match_inport=True): + group_id = flowrule.get('next_group_id', None) + next_hops = flowrule.get('next_hops', None) + global_vlan_tag = flowrule['segment_id'] + + # if the group is not none, install the egress rule for this SF + if ( + group_id and next_hops + ): + # 1st, install br-int flow rule on table ACROSS_SUBNET_TABLE + # and group table + buckets = [] + # A2 Group Creation + for item in next_hops: + bucket = ( + 'bucket=weight=%d, mod_dl_dst:%s,' + 'resubmit(,%d)' % ( + item['weight'], + item['mac_address'], + ACROSS_SUBNET_TABLE + ) + ) + buckets.append(bucket) + # A3 In table 5, add MPLS header and send to either patch port + # or table 10 for remote and local node respectively. + subnet_actions_list = [] + + priority = 30 + if item['local_endpoint'] == self.local_host: + subnet_actions = ( + "mod_vlan_vid:%d, resubmit(,%d)" % (global_vlan_tag, + INGRESS_TABLE)) + else: + # same subnet with next hop + subnet_actions = "output:%s" % self.phy_patch_ofport + subnet_actions_list.append(subnet_actions) + + self.br_int.add_flow( + table=ACROSS_SUBNET_TABLE, + priority=priority, + dl_dst=item['mac_address'], + dl_type=0x0800, + actions="%s" % ','.join(subnet_actions_list)) + + buckets = ','.join(buckets) + group_content = self.br_int.dump_group_for_id(group_id) + if group_content.find('group_id=%d' % group_id) == -1: + self.br_int.add_group(group_id=group_id, + type='select', buckets=buckets) + else: + self.br_int.mod_group(group_id=group_id, + type='select', buckets=buckets) + + self._setup_source_based_flows( + flowrule, + flowrule['add_fcs'], + add_flow=True, + match_inport=True) + + def _update_flows(self, table, priority, + match_info, actions=None, add_flow=True): + if add_flow: + self.br_int.add_flow(table=table, + priority=priority, + actions=actions, + **match_info) + else: + self.br_int.delete_flows(table=table, + priority=priority, + **match_info) + + def _check_if_local_port(self, port_id): + try: + if self.br_int.get_vif_port_by_id(port_id): + return True + except Exception: + pass + return False + + def _setup_source_based_flows( + self, flowrule, flow_classifier_list, + add_flow=True, match_inport=True + ): + inport_match = {} + priority = 50 + global_vlan_tag = flowrule['segment_id'] + local_vlan_tag = self._get_vlan_by_port(flowrule['egress']) + + if match_inport is True: + egress_port = self.br_int.get_vif_port_by_id(flowrule['egress']) + if egress_port: + inport_match = dict(in_port=egress_port.ofport) + + group_id = flowrule.get('next_group_id') + next_hops = flowrule.get('next_hops') + if not (group_id and next_hops): + local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) + egress_mac = egress_port.vif_mac + # B6. For packets coming out of SF, we resubmit to table 5. + match_info = dict(dl_type=0x0800, **inport_match) + actions = ("resubmit(,%s)" % ACROSS_SUBNET_TABLE) + + self._update_flows(ovs_consts.LOCAL_SWITCHING, 60, + match_info, actions, add_flow) + + ingress_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) + ingress_mac = ingress_port.vif_mac + # B7 In table 5, we decide whether to send it locally or remotely. + for fc in flow_classifier_list: + ldp_port_id = fc['logical_destination_port'] + ldp_mac = fc['ldp_mac_address'] + + if self._check_if_local_port(ldp_port_id): + ldp_port = self.br_int.get_vif_port_by_id(ldp_port_id) + ldp_ofport = ldp_port.ofport + actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( + ldp_mac, ldp_ofport)) + else: + actions = ("mod_vlan_vid:%s, mod_dl_src:%s, " + "mod_dl_dst:%s, output:%s" % ( + (local_vlan_tag, egress_mac, + ldp_mac, self.phy_patch_ofport))) + + match_info = dict(nw_dst=fc['destination_ip_prefix'], + dl_dst=ingress_mac, + dl_type=0x0800, + dl_vlan=global_vlan_tag) + + self._update_flows(ACROSS_SUBNET_TABLE, priority, + match_info, actions, add_flow) + return + + # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. + # Action is redirect to group. + ldp_mac = flow_classifier_list[0]['ldp_mac_address'] + actions = ("mod_vlan_vid:%s, group:%d" % ( + local_vlan_tag, group_id)) + match_info = dict(inport_match, **{'dl_dst': ldp_mac, + 'dl_type': '0x0800'}) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_info, actions, add_flow) + + def _get_port_info(self, port_id, info_type): + ''' Returns specific port info + + @param port_id: Neutron port id + @param info_type: Type is List [mac,ofport,vlan] + @return: Tuple (MAC address, openflow port number) + + ''' + + res = () + port = self.br_int.get_vif_port_by_id(port_id) + + port_type_map = { + 'mac': port.vif_mac, + 'ofport': port.ofport, + 'vlan': self._get_vlan_by_port(port_id)} + + for each in info_type: + res += (port_type_map[each],) + + return res + + def _get_vlan_by_port(self, port_id): + try: + net_uuid = self.vlan_manager.get_net_uuid(port_id) + return self.vlan_manager.get(net_uuid).vlan + except (vlanmanager.VifIdNotFound, vlanmanager.MappingNotFound): + return None + + def _setup_destination_based_flows(self, flowrule, + flow_classifier_list, + add_flow=True): + priority = 50 + ingress_mac, ingress_ofport = self._get_port_info( + flowrule['ingress'], ['mac', 'ofport']) + global_vlan_tag = flowrule['segment_id'] + + group_id = flowrule.get('next_group_id') + next_hops = flowrule.get('next_hops') + if not (group_id and next_hops): + # B5. At ingress of SF, if dl_dst belongs to SF and nw_dst + # belongs to Dest VM, output to ingress port of SF. + for fc in flow_classifier_list: + dst_ip = fc['destination_ip_prefix'] + + match_info = dict(dl_type=0x0800, + dl_vlan=global_vlan_tag, + dl_dst=ingress_mac) + actions = ("strip_vlan, output:%s" % (ingress_ofport)) + + self._update_flows(INGRESS_TABLE, 60, + match_info, actions, add_flow) + + # B4. At ingress of SF, if dest mac matches with SF ingress, + # vlan matches, then resubmit to 10. + # This is per ldp because ldps can have different vlan tags. + match_info = dict( + dl_type=0x0800, in_port=self.phy_patch_ofport, + dl_vlan=global_vlan_tag, dl_dst=ingress_mac) + actions = ("resubmit(,%s)" % INGRESS_TABLE) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_info, actions, add_flow) + return + + # B9. Match IP packets with vlan and source IP. Actions will be to + # strip vlan and modify src MAC and output to Dest VM port. + nw_src = flow_classifier_list[0]['source_ip_prefix'] + + src_port_mac = flow_classifier_list[0]['lsp_mac_address'] + actions = ('strip_vlan, mod_dl_src:%s, output:%s' % ( + src_port_mac, ingress_ofport)) + + match_field = dict( + dl_type=0x0800, + dl_vlan=global_vlan_tag, + dl_dst=ingress_mac, + nw_src=nw_src) + + self._update_flows(10, priority, + match_field, actions, add_flow) + + # B8. At ingress of dest, match ip packet with vlan tag and dest + # MAC address. Action will be to resubmit to 10. + match_field = dict( + dl_type=0x0800, + dl_vlan=global_vlan_tag, + dl_dst=ingress_mac) + actions = ("resubmit(,%s)" % 10) + + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_field, actions, add_flow) + + def _setup_ingress_flow_rules(self, flowrule): + flow_classifier_list = flowrule['add_fcs'] + self._setup_destination_based_flows(flowrule, + flow_classifier_list) diff --git a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py index d0ce1a54..031670c4 100644 --- a/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/openvswitch/sfc_driver.py @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy - from neutron_lib import constants as n_consts from oslo_config import cfg from oslo_log import log as logging @@ -51,11 +49,6 @@ PC_DEF_PRI = 20 PC_INGRESS_PRI = 30 -sfc_ovs_opt = [ - cfg.StrOpt('local_hostname', - default='', help='Hostname of the local machine')] -cfg.CONF.register_opts(sfc_ovs_opt, 'OVS') - class SfcOVSAgentDriver(sfc.SfcAgentDriver): """This class will support MPLS frame @@ -80,7 +73,6 @@ def __init__(self): self.br_int = None self.local_ip = None - self.local_host = None self.patch_tun_ofport = None self.vlan_manager = None @@ -93,10 +85,8 @@ def initialize(self): self.br_int.set_protocols(SfcOVSAgentDriver.REQUIRED_PROTOCOLS) self.local_ip = cfg.CONF.OVS.local_ip - self.local_host = cfg.CONF.OVS.local_hostname - '''self.patch_tun_ofport = self.br_int.get_port_ofport( - cfg.CONF.OVS.int_peer_patch_port)''' - self.patch_tun_ofport = self.br_int.get_port_ofport("int-br-vlan") + self.patch_tun_ofport = self.br_int.get_port_ofport( + cfg.CONF.OVS.int_peer_patch_port) self.vlan_manager = vlanmanager.LocalVlanManager() self._clear_sfc_flow_on_int_br() @@ -105,10 +95,8 @@ def update_flow_rules(self, flowrule, flowrule_status): try: if flowrule.get('egress', None): self._setup_egress_flow_rules(flowrule) - self._setup_reverse_ingress_flow_rules(flowrule) if flowrule.get('ingress', None): self._setup_ingress_flow_rules_with_mpls(flowrule) - self._setup_reverse_egress_flow_rules(flowrule) flowrule_status_temp = {} flowrule_status_temp['id'] = flowrule['id'] @@ -127,7 +115,6 @@ def delete_flow_rule(self, flowrule, flowrule_status): LOG.debug("delete_flow_rule, flowrule = %s", flowrule) - node_type = flowrule['node_type'] # delete tunnel table flow rule on br-int(egress match) if flowrule['egress'] is not None: self._setup_local_switch_flows_on_int_br( @@ -137,8 +124,6 @@ def delete_flow_rule(self, flowrule, flowrule_status): add_flow=False, match_inport=True ) - self._setup_source_based_flows( - flowrule, flowrule['del_fcs'], add_flow=False) # delete group table, need to check again group_id = flowrule.get('next_group_id', None) if group_id and flowrule.get('group_refcnt', None) <= 1: @@ -149,9 +134,6 @@ def delete_flow_rule(self, flowrule, flowrule_status): dl_dst=item['mac_address']) if flowrule['ingress'] is not None: - self._setup_destination_based_forwarding(flowrule, - flowrule['del_fcs'], - add_flow=False) # delete table INGRESS_TABLE ingress match flow rule # on br-int(ingress match) vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) @@ -164,17 +146,6 @@ def delete_flow_rule(self, flowrule, flowrule_status): dl_dst=vif_port.vif_mac, mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1) ) - if flowrule.get('reverse_path'): - rev_flowrule = self._reverse_flow_rules(flowrule, node_type) - if (flowrule['ingress'] is not None or ( - node_type == ovs_consts.SF_NODE)): - self._setup_source_based_flows( - rev_flowrule, rev_flowrule['del_fcs'], add_flow=False) - if (flowrule['egress'] is not None or ( - node_type == ovs_consts.SF_NODE)): - self._setup_destination_based_forwarding( - rev_flowrule, rev_flowrule['del_fcs'], - add_flow=False) except Exception as e: flowrule_status_temp = {} flowrule_status_temp['id'] = flowrule['id'] @@ -183,41 +154,6 @@ def delete_flow_rule(self, flowrule, flowrule_status): LOG.exception(e) LOG.error(_LE("delete_flow_rule failed")) - def _reverse_flow_rules(self, flowrule, node_type): - rev_flowrule = copy.deepcopy(flowrule) - - def _reverse_fcs(op): - for fc in rev_flowrule[op]: - fc['logical_destination_port'], fc['logical_source_port'] = ( - fc['logical_source_port'], fc['logical_destination_port']) - fc['ldp_mac_address'], fc['lsp_mac_address'] = ( - fc['lsp_mac_address'], fc['ldp_mac_address']) - fc['destination_ip_prefix'], fc['source_ip_prefix'] = ( - fc['source_ip_prefix'], fc['destination_ip_prefix']) - - for op in ['add_fcs', 'del_fcs']: - _reverse_fcs(op) - - if node_type == ovs_consts.SRC_NODE: - rev_flowrule['ingress'], rev_flowrule['egress'] = ( - rev_flowrule['egress'], rev_flowrule['ingress']) - - return rev_flowrule - - def _setup_reverse_ingress_flow_rules(self, flowrule): - if not flowrule['reverse_path']: - return - rev_flowrule = self._reverse_flow_rules(flowrule, - flowrule['node_type']) - self._setup_ingress_flow_rules_with_mpls(rev_flowrule) - - def _setup_reverse_egress_flow_rules(self, flowrule): - if not flowrule['reverse_path']: - return - rev_flowrule = self._reverse_flow_rules(flowrule, - flowrule['node_type']) - self._setup_egress_flow_rules(rev_flowrule) - def _clear_sfc_flow_on_int_br(self): self.br_int.delete_group(group_id='all') self.br_int.delete_flows(table=ACROSS_SUBNET_TABLE) @@ -376,7 +312,6 @@ def _setup_local_switch_flows_on_int_br( def _setup_egress_flow_rules(self, flowrule, match_inport=True): group_id = flowrule.get('next_group_id', None) next_hops = flowrule.get('next_hops', None) - global_vlan_tag = flowrule['segment_id'] # if the group is not none, install the egress rule for this SF if ( @@ -385,8 +320,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): # 1st, install br-int flow rule on table ACROSS_SUBNET_TABLE # and group table buckets = [] - local_vlan = self._get_vlan_by_port(flowrule['egress']) - # A2 Group Creation + vlan = self._get_vlan_by_port(flowrule['egress']) for item in next_hops: bucket = ( 'bucket=weight=%d, mod_dl_dst:%s,' @@ -397,15 +331,19 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): ) ) buckets.append(bucket) - # A3 In table 5, add MPLS header and send to either patch port - # or table 10 for remote and local node respectively. subnet_actions_list = [] - - priority = 30 - if item['local_endpoint'] == self.local_host: + push_mpls = ( + "push_mpls:0x8847," + "set_mpls_label:%d," + "set_mpls_ttl:%d," + "mod_vlan_vid:%d," % + ((flowrule['nsp'] << 8) | flowrule['nsi'], + flowrule['nsi'], vlan)) + subnet_actions_list.append(push_mpls) + + if item['local_endpoint'] == self.local_ip: subnet_actions = ( - "mod_vlan_vid:%d, resubmit(,%d)" % (global_vlan_tag, - INGRESS_TABLE)) + "resubmit(,%d)" % INGRESS_TABLE) else: # same subnet with next hop subnet_actions = "output:%s" % self.patch_tun_ofport @@ -413,7 +351,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): self.br_int.add_flow( table=ACROSS_SUBNET_TABLE, - priority=priority, + priority=0, dl_dst=item['mac_address'], dl_type=0x0800, actions="%s" % ','.join(subnet_actions_list)) @@ -426,119 +364,41 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): else: self.br_int.mod_group(group_id=group_id, type='select', buckets=buckets) - else: - pass - - self._setup_source_based_flows( - flowrule, - flowrule['add_fcs'], - add_flow=True, - match_inport=True) - - def _update_flows(self, table, priority, - match_info, actions=None, add_flow=True): - if add_flow: - self.br_int.add_flow(table=table, - priority=priority, - actions=actions, - **match_info) - else: - self.br_int.delete_flows(table=table, - priority=priority, - **match_info) - - def _check_if_local_port(self, port_id): - try: - if self.br_int.get_vif_port_by_id(port_id): - return True - except Exception: - pass - return False - - def _setup_source_based_flows( - self, flowrule, flow_classifier_list, - add_flow=True, match_inport=True - ): - inport_match = {} - priority = 50 - global_vlan_tag = flowrule['segment_id'] - local_vlan_tag = self._get_vlan_by_port(flowrule['egress']) - - if match_inport is True: - egress_port = self.br_int.get_vif_port_by_id(flowrule['egress']) - if egress_port: - inport_match = dict(in_port=egress_port.ofport) - - group_id = flowrule.get('next_group_id') - next_hops = flowrule.get('next_hops') - if not (group_id and next_hops): - local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) - egress_mac = egress_port.vif_mac - # B6. For packets coming out of SF, we resubmit to table 5. - match_info = dict(dl_type=0x0800, **inport_match) - actions = ("resubmit(,%s)" % ACROSS_SUBNET_TABLE) - - self._update_flows(ovs_consts.LOCAL_SWITCHING, 60, - match_info, actions, add_flow) - - ingress_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) - ingress_mac = ingress_port.vif_mac - # B7 In table 5, we decide whether to send it locally or remotely. - for fc in flow_classifier_list: - ldp_port_id = fc['logical_destination_port'] - ldp_mac = fc['ldp_mac_address'] - - if self._check_if_local_port(ldp_port_id): - ldp_port = self.br_int.get_vif_port_by_id(ldp_port_id) - ldp_ofport = ldp_port.ofport - actions = ("strip_vlan, mod_dl_dst:%s, output:%s" % ( - ldp_mac, ldp_ofport)) - else: - actions = ("mod_vlan_vid:%s, mod_dl_src:%s, " - "mod_dl_dst:%s, output:%s" % ( - (local_vlan_tag, egress_mac, - ldp_mac, self.patch_tun_ofport))) - - match_info = dict(nw_dst=fc['destination_ip_prefix'], - dl_dst=ingress_mac, - dl_type=0x0800, - dl_vlan=global_vlan_tag) - - self._update_flows(ACROSS_SUBNET_TABLE, priority, - match_info, actions, add_flow) - return - - # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. - # Action is redirect to group. - ldp_mac = flow_classifier_list[0]['ldp_mac_address'] - actions = ("mod_vlan_vid:%s, group:%d" % ( - local_vlan_tag, group_id)) - match_info = dict(inport_match, **{'dl_dst': ldp_mac, - 'dl_type': '0x0800'}) - self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, - match_info, actions, add_flow) - - def _get_port_info(self, port_id, info_type): - ''' Returns specific port info - @param port_id: Neutron port id - @param info_type: Type is List [mac,ofport,vlan] - @return: Tuple (MAC address, openflow port number) - - ''' - - res = () - port = self.br_int.get_vif_port_by_id(port_id) - - port_type_map = { - 'mac': port.vif_mac, - 'ofport': port.ofport, - 'vlan': self._get_vlan_by_port(port_id)} - - for each in info_type: - res += (port_type_map[each],) + # 2nd, install br-int flow rule on table 0 for egress traffic + # for egress traffic + enc_actions = ("group:%d" % group_id) + # to uninstall the removed flow classifiers + self._setup_local_switch_flows_on_int_br( + flowrule, + flowrule['del_fcs'], + None, + add_flow=False, + match_inport=match_inport) + # to install the added flow classifiers + self._setup_local_switch_flows_on_int_br( + flowrule, + flowrule['add_fcs'], + enc_actions, + add_flow=True, + match_inport=match_inport) + else: + # to uninstall the new removed flow classifiers + self._setup_local_switch_flows_on_int_br( + flowrule, + flowrule['del_fcs'], + None, + add_flow=False, + match_inport=True + ) - return res + # to install the added flow classifiers + self._setup_local_switch_flows_on_int_br( + flowrule, + flowrule['add_fcs'], + actions='normal', + add_flow=True, + match_inport=True) def _get_vlan_by_port(self, port_id): try: @@ -547,73 +407,22 @@ def _get_vlan_by_port(self, port_id): except (vlanmanager.VifIdNotFound, vlanmanager.MappingNotFound): return None - def _setup_destination_based_forwarding(self, flowrule, - flow_classifier_list, - add_flow=True): - priority = 50 - ingress_mac, ingress_ofport = self._get_port_info( - flowrule['ingress'], ['mac', 'ofport']) - local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) - global_vlan_tag = flowrule['segment_id'] - - group_id = flowrule.get('next_group_id') - next_hops = flowrule.get('next_hops') - if not (group_id and next_hops): - # B5. At ingress of SF, if dl_dst belongs to SF and nw_dst - # belongs to Dest VM, output to ingress port of SF. - for fc in flow_classifier_list: - ldp_mac = fc['ldp_mac_address'] - dst_ip = fc['destination_ip_prefix'] - - match_info = dict(dl_type=0x0800, - dl_vlan=global_vlan_tag, - dl_dst=ingress_mac, - nw_dst=dst_ip) - actions = ("strip_vlan, output:%s" % (ingress_ofport)) - - self._update_flows(INGRESS_TABLE, 60, - match_info, actions, add_flow) - - # B4. At ingress of SF, if dest mac matches with SF ingress, - # vlan matches, then resubmit to 10. - # This is per ldp because ldps can have different vlan tags. - match_info = dict( - dl_type=0x0800, - dl_vlan=global_vlan_tag, dl_dst=ingress_mac) - actions = ("resubmit(,%s)" % INGRESS_TABLE) - self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, - match_info, actions, add_flow) - return - - # B9. Match IP packets with vlan and source IP. Actions will be to - # strip vlan and modify src MAC and output to Dest VM port. - nw_src = flow_classifier_list[0]['source_ip_prefix'] - - src_port_mac = flow_classifier_list[0]['lsp_mac_address'] - actions = ('strip_vlan, mod_dl_src:%s, output:%s' % ( - src_port_mac, ingress_ofport)) - - match_field = dict( - dl_type=0x0800, - dl_vlan=global_vlan_tag, - dl_dst=ingress_mac, - nw_src=nw_src) - - self._update_flows(10, priority, - match_field, actions, add_flow) - - # B8. At ingress of dest, match ip packet with vlan tag and dest - # MAC address. Action will be to resubmit to 10. - match_field = dict( - dl_type=0x0800, - dl_vlan=global_vlan_tag, - dl_dst=ingress_mac) - actions = ("resubmit(,%s)" % 10) - - self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, - match_field, actions, add_flow) - def _setup_ingress_flow_rules_with_mpls(self, flowrule): - flow_classifier_list = flowrule['add_fcs'] - self._setup_destination_based_forwarding(flowrule, - flow_classifier_list) + vif_port = self.br_int.get_vif_port_by_id(flowrule['ingress']) + if vif_port: + vlan = self._get_vlan_by_port(flowrule['ingress']) + # install br-int flow rule on table 0 for ingress traffic + match_field = {} + + actions = ("strip_vlan, pop_mpls:0x0800," + "output:%s" % vif_port.ofport) + match_field = dict( + table=INGRESS_TABLE, + priority=1, + dl_dst=vif_port.vif_mac, + dl_vlan=vlan, + dl_type=0x8847, + mpls_label=flowrule['nsp'] << 8 | (flowrule['nsi'] + 1), + actions=actions) + + self.br_int.add_flow(**match_field) diff --git a/networking_sfc/services/sfc/drivers/oc/driver.py b/networking_sfc/services/sfc/drivers/oc/driver.py index e02a81c2..1b8386e7 100644 --- a/networking_sfc/services/sfc/drivers/oc/driver.py +++ b/networking_sfc/services/sfc/drivers/oc/driver.py @@ -1,42 +1,566 @@ +# Copyright e015 nuturewei. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import netaddr + from oslo_log import helpers as log_helpers +from oslo_log import log as logging +from oslo_serialization import jsonutils + +from neutron import manager + +from neutron.plugins.common import constants as np_const + +from networking_sfc._i18n import _LE, _LW +from networking_sfc.services.sfc.common import exceptions as exc +from networking_sfc.services.sfc.drivers.ovs import ( + constants as ovs_const) +from networking_sfc.services.sfc.drivers.ovs import driver as sfc_dvr -from networking_sfc.services.flowclassifier.common import exceptions as exc -from networking_sfc.services.flowclassifier.drivers import base as fc_driver +LOG = logging.getLogger(__name__) -class OVSFlowClassifierDriver(fc_driver.FlowClassifierDriverBase): - """FlowClassifier Driver Base Class.""" + +class OCSfcDriver(sfc_dvr.OVSSfcDriver): + """Sfc Driver Base Class.""" def initialize(self): - pass + super(OCSfcDriver, self).initialize() @log_helpers.log_method_call - def create_flow_classifier(self, context): - pass + def _add_flowclassifier_port_assoc(self, fc_ids, tenant_id, + src_node): + for fc in self._get_fcs_by_ids(fc_ids): + need_assoc = True + src_pd_filter = dst_pd_filter = None + + if fc['logical_source_port']: + # lookup the source port + src_pd_filter = dict( + egress=fc['logical_source_port'], + tenant_id=tenant_id + ) + if fc['logical_destination_port']: + # lookup the source port + dst_pd_filter = dict( + ingress=fc['logical_destination_port'], + tenant_id=tenant_id + ) + src_pd = self.get_port_detail_by_filter(src_pd_filter) + dst_pd = self.get_port_detail_by_filter(dst_pd_filter) + + new_src_pd = new_dst_pd = '' + if not (src_pd and dst_pd): + if not src_pd: + # Create source port detail + new_src_pd = self._create_port_detail(src_pd_filter) + LOG.debug('create src port detail: %s', new_src_pd) + if not dst_pd: + # Create destination port detail + new_dst_pd = self._create_port_detail(dst_pd_filter) + LOG.debug('create dst port detail: %s', new_dst_pd) + else: + for path_node in src_pd['path_nodes']: + if path_node['pathnode_id'] == src_node['id']: + need_assoc = False + if need_assoc: + # Create associate relationship + if new_src_pd: + assco_args = { + 'portpair_id': new_src_pd['id'], + 'pathnode_id': src_node['id'], + 'weight': 1, + } + sna = self.create_pathport_assoc(assco_args) + LOG.debug('create assoc src port with node: %s', sna) + src_node['portpair_details'].append(new_src_pd['id']) + if new_dst_pd: + assco_args = { + 'portpair_id': new_dst_pd['id'], + 'pathnode_id': src_node['id'], + 'weight': 1, + } + sna = self.create_pathport_assoc(assco_args) + LOG.debug('create assoc src port with node: %s', sna) + src_node['portpair_details'].append(new_dst_pd['id']) + + def _create_src_and_dest_nodes(self, port_chain, next_group_intid, + next_group_members, path_nodes): + path_id = port_chain['chain_id'] + port_pair_groups = port_chain['port_pair_groups'] + sf_path_length = len(port_pair_groups) + + # Create a head node object for port chain + src_args = {'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.SRC_NODE, + 'nsp': path_id, + 'nsi': 0xff, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': next_group_intid, + 'next_hop': jsonutils.dumps(next_group_members), + } + src_node = self.create_path_node(src_args) + LOG.debug('create src node: %s', src_node) + path_nodes.append(src_node) + + # Create a destination node object for port chain + dst_args = { + 'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.DST_NODE, + 'nsp': path_id, + 'nsi': 0xff - sf_path_length - 1, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': None, + 'next_hop': None + } + dst_node = self.create_path_node(dst_args) + LOG.debug('create dst node: %s', dst_node) + path_nodes.append(dst_node) + + return src_node + + def _check_if_bi_node(self, src_node): + ''' We assume that if the flow classifiers associated with + a port chain contain both LSP and LDP, we treat it as a bi-directional + chain. LSP and LDP can be part of same or different flow + classifier(s) ''' + + is_lsp = False + is_ldp = False + portpair_details = src_node['portpair_details'] + for each in portpair_details: + port_detail = self.get_port_detail_by_filter(dict(id=each)) + if port_detail['egress']: + is_lsp = True + else: + is_ldp = True + if is_lsp and is_ldp: + return True + return False @log_helpers.log_method_call - def update_flow_classifier(self, context): - pass + def _create_portchain_path(self, context, port_chain): + path_nodes = [] + # Create an assoc object for chain_id and path_id + # context = context._plugin_context + path_id = port_chain['chain_id'] + + if not path_id: + LOG.error(_LE('No path_id available for creating port chain path')) + return + + port_pair_groups = port_chain['port_pair_groups'] + sf_path_length = len(port_pair_groups) + + # Detect cross-subnet transit + # Compare subnets for logical source ports + # and first PPG ingress ports + '''for fc in self._get_fcs_by_ids(port_chain['flow_classifiers']): + if fc['logical_source_port']: + subnet1 = self._get_subnet_by_port(fc['logical_source_port']) + else: + subnet1 = self._get_subnet_by_port(fc[ + 'logical_destination_port']) + cidr1 = subnet1['cidr'] + ppg = context._plugin.get_port_pair_group(context._plugin_context, + port_pair_groups[0]) + for pp_id1 in ppg['port_pairs']: + pp1 = context._plugin.get_port_pair(context._plugin_context, + pp_id1) + filter1 = {} + if pp1.get('ingress', None): + filter1 = dict(dict(ingress=pp1['ingress']), **filter1) + pd1 = self.get_port_detail_by_filter(filter1) + subnet2 = self._get_subnet_by_port(pd1['ingress']) + cidr2 = subnet2['cidr'] + if cidr1 != cidr2: + LOG.error(_LE('Cross-subnet chain not supported')) + raise exc.SfcDriverError() + return None''' + + # Compare subnets for PPG egress ports + # and next PPG ingress ports + for i in range(sf_path_length - 1): + ppg = context._plugin.get_port_pair_group(context._plugin_context, + port_pair_groups[i]) + next_ppg = context._plugin.get_port_pair_group( + context._plugin_context, port_pair_groups[i + 1]) + for pp_id1 in ppg['port_pairs']: + pp1 = context._plugin.get_port_pair(context._plugin_context, + pp_id1) + filter1 = {} + if pp1.get('egress', None): + filter1 = dict(dict(egress=pp1['egress']), **filter1) + pd1 = self.get_port_detail_by_filter(filter1) + subnet1 = self._get_subnet_by_port(pd1['egress']) + cidr3 = subnet1['cidr'] + + for pp_id2 in next_ppg['port_pairs']: + pp2 = context._plugin.get_port_pair( + context._plugin_context, pp_id2) + filter2 = {} + if pp2.get('ingress', None): + filter2 = dict(dict(ingress=pp2['ingress']), **filter2) + pd2 = self.get_port_detail_by_filter(filter2) + subnet2 = self._get_subnet_by_port(pd2['ingress']) + cidr4 = subnet2['cidr'] + if cidr3 != cidr4: + LOG.error(_LE('Cross-subnet chain not supported')) + raise exc.SfcDriverError() + return None + + next_group_intid, next_group_members = self._get_portgroup_members( + context, port_chain['port_pair_groups'][0]) + + src_node = self._create_src_and_dest_nodes( + port_chain, next_group_intid, + next_group_members, path_nodes) + + self._add_flowclassifier_port_assoc( + port_chain['flow_classifiers'], + port_chain['tenant_id'], + src_node + ) + + is_bi_node = self._check_if_bi_node(src_node) + + if is_bi_node: + path_nodes[0].update(dict(reverse_path=True)) + else: + path_nodes[0].update(dict(reverse_path=False)) + + for i in range(sf_path_length): + cur_group_members = next_group_members + # next_group for next hop + if i < sf_path_length - 1: + next_group_intid, next_group_members = ( + self._get_portgroup_members( + context, port_pair_groups[i + 1]) + ) + else: + next_group_intid = None + next_group_members = None + + # Create a node object + node_args = { + 'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.SF_NODE, + 'nsp': path_id, + 'nsi': 0xfe - i, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': next_group_intid, + 'next_hop': ( + None if not next_group_members else + jsonutils.dumps(next_group_members) + ) + } + sf_node = self.create_path_node(node_args) + LOG.debug('chain path node: %s', sf_node) + + # If Src Node is bi, then SF node shall naturally be bi. + if is_bi_node: + sf_node.update(dict(reverse_path=True)) + else: + sf_node.update(dict(reverse_path=False)) + # Create the assocation objects that combine the pathnode_id with + # the ingress of the port_pairs in the current group + # when port_group does not reach tail + for member in cur_group_members: + assco_args = {'portpair_id': member['portpair_id'], + 'pathnode_id': sf_node['id'], + 'weight': member['weight'], } + sfna = self.create_pathport_assoc(assco_args) + LOG.debug('create assoc port with node: %s', sfna) + sf_node['portpair_details'].append(member['portpair_id']) + path_nodes.append(sf_node) + + return path_nodes + + def _delete_path_node_port_flowrule(self, node, port, fc_ids): + # if this port is not binding, don't to generate flow rule + if not port['host_id']: + return + flow_rule = self._build_portchain_flowrule_body( + node, + port, + None, + fc_ids) + + if flow_rule['reverse_path']: + if (flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule[ + 'ingress']): + flow_rule = self._reverse_flow_rules(flow_rule) + + LOG.info("ZZZ FLOW RULES %r" % flow_rule) + self.ovs_driver_rpc.ask_agent_to_delete_flow_rules( + self.admin_context, + flow_rule) + + self._delete_agent_fdb_entries(flow_rule) + + def _delete_path_node_flowrule(self, node, fc_ids): + if node['portpair_details'] is None: + return + for each in node['portpair_details']: + port = self.get_port_detail_by_filter(dict(id=each)) + if port: + if node['node_type'] == ovs_const.SF_NODE: + _, egress = self._get_ingress_egress_tap_ports(port) + port.update({'egress': egress}) + self._delete_path_node_port_flowrule( + node, port, fc_ids) @log_helpers.log_method_call - def delete_flow_classifier(self, context): - pass + def _delete_portchain_path(self, context, port_chain): + pds = self.get_path_nodes_by_filter( + dict(portchain_id=port_chain['id'])) + src_node = None + is_bi_node = True + if pds: + for pd in pds: + ''' We cannot assume that Src node always comes first. + # If Src Node is bi, then SF node shall naturally be bi. Here, + # we assume that Src node will be the first in 'pds'. + if self._check_if_bi_node(pd): + is_bi_node = True''' + if is_bi_node: + pd.update(dict(reverse_path=True)) + else: + pd.update(dict(reverse_path=False)) + + if pd['node_type'] == ovs_const.SRC_NODE: + src_node = pd + + self._delete_path_node_flowrule( + pd, + port_chain['flow_classifiers'] + ) + for pd in pds: + self.delete_path_node(pd['id']) + + # delete the ports on the traffic classifier + self._remove_flowclassifier_port_assoc( + port_chain['flow_classifiers'], + port_chain['tenant_id'], + src_node + ) + + def _filter_flow_classifiers(self, flow_rule, fc_ids): + """Filter flow classifiers. + + @return: list of the flow classifiers + """ + + fc_return = [] + core_plugin = manager.NeutronManager.get_plugin() + + if not fc_ids: + return fc_return + fcs = self._get_fcs_by_ids(fc_ids) + for fc in fcs: + new_fc = fc.copy() + new_fc.pop('id') + new_fc.pop('name') + new_fc.pop('tenant_id') + new_fc.pop('description') + + lsp = new_fc.get('logical_source_port') + ldp = new_fc.get('logical_destination_port') + if lsp: + port_detail = core_plugin.get_port(self.admin_context, lsp) + new_fc['lsp_mac_address'] = port_detail['mac_address'] + new_fc['source_ip_prefix'] = port_detail[ + 'fixed_ips'][0]['ip_address'] + if ldp: + port_detail = core_plugin.get_port(self.admin_context, ldp) + new_fc['ldp_mac_address'] = port_detail['mac_address'] + new_fc['destination_ip_prefix'] = port_detail[ + 'fixed_ips'][0]['ip_address'] + + if ( + flow_rule['node_type'] in [ovs_const.SRC_NODE] and + flow_rule['egress'] == fc['logical_source_port'] + ) or ( + flow_rule['node_type'] in [ovs_const.SRC_NODE] and + flow_rule['ingress'] == fc['logical_destination_port'] + ): + fc_return.append(new_fc) + elif flow_rule['node_type'] in [ovs_const.SF_NODE]: + fc_return.append(new_fc) + + return fc_return + + def _reverse_flow_rules(self, flowrule): + + def _reverse_fcs(op): + for fc in flowrule[op]: + fc['logical_destination_port'], fc['logical_source_port'] = ( + fc['logical_source_port'], fc['logical_destination_port']) + fc['ldp_mac_address'], fc['lsp_mac_address'] = ( + fc['lsp_mac_address'], fc['ldp_mac_address']) + fc['destination_ip_prefix'], fc['source_ip_prefix'] = ( + fc['source_ip_prefix'], fc['destination_ip_prefix']) + + for op in ['add_fcs', 'del_fcs']: + _reverse_fcs(op) + + flowrule['ingress'], flowrule['egress'] = ( + flowrule['egress'], flowrule['ingress']) + + return flowrule + + def _update_path_node_port_flowrules(self, node, port, + add_fc_ids=None, del_fc_ids=None): + # if this port is not binding, don't to generate flow rule + if not port['host_id']: + return + + flow_rule = self._build_portchain_flowrule_body( + node, + port, + add_fc_ids, + del_fc_ids) + + if flow_rule['reverse_path']: + if (flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule[ + 'ingress']): + flow_rule = self._reverse_flow_rules(flow_rule) + + LOG.info("YYY FLOW RULES %r" % flow_rule) + self.ovs_driver_rpc.ask_agent_to_update_flow_rules( + self.admin_context, + flow_rule) + + self._update_agent_fdb_entries(flow_rule) + + def _update_path_node_flowrules(self, node, + add_fc_ids=None, del_fc_ids=None): + if node['portpair_details'] is None: + return + for each in node['portpair_details']: + port = self.get_port_detail_by_filter(dict(id=each)) + + if port: + if node['node_type'] == ovs_const.SF_NODE: + _, egress = self._get_ingress_egress_tap_ports(port) + port.update({'egress': egress}) + self._update_path_node_port_flowrules( + node, port, add_fc_ids, del_fc_ids) @log_helpers.log_method_call - def create_flow_classifier_precommit(self, context): - """OVS Driver precommit before transaction committed. + def create_port_chain(self, context): + port_chain = context.current + path_nodes = self._create_portchain_path(context, port_chain) + LOG.info("XXX PATH NODES %r" % path_nodes) + self._update_path_nodes( + path_nodes, + port_chain['flow_classifiers'], + None) - Make sure the logical_source_port is not None. - Make sure the logical_destination_port is None. + @log_helpers.log_method_call + def _get_portpair_detail_info(self, portpair_id): + """Get port detail. + + @param: portpair_id: uuid + @return: (host_id, local_ip, network_type, segment_id, + service_insert_type): tuple """ - flow_classifier = context.current - logical_source_port = flow_classifier['logical_source_port'] - if logical_source_port is None: - raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s does not set ' - 'logical source port in ovs driver' % flow_classifier['id'])) - logical_destination_port = flow_classifier['logical_destination_port'] - if logical_destination_port is not None: - raise exc.FlowClassifierBadRequest(message=( - 'FlowClassifier %s sets logical destination port ' - 'in ovs driver' % flow_classifier['id'])) + + core_plugin = manager.NeutronManager.get_plugin() + port_detail = core_plugin.get_port(self.admin_context, portpair_id) + host_id, local_ip, network_type, segment_id, mac_address = ( + (None, ) * 5) + + if port_detail: + host_id = port_detail['binding:host_id'] + network_id = port_detail['network_id'] + mac_address = port_detail['mac_address'] + network_info = core_plugin.get_network( + self.admin_context, network_id) + network_type = network_info['provider:network_type'] + segment_id = network_info['provider:segmentation_id'] + + if network_type not in [np_const.TYPE_VXLAN, np_const.TYPE_VLAN]: + LOG.warning(_LW("Currently only support vxlan and vlan networks")) + return ((None, ) * 5) + elif not host_id: + LOG.warning(_LW("This port has not been binding")) + return ((None, ) * 5) + else: + if network_type == np_const.TYPE_VXLAN: + driver = core_plugin.type_manager.drivers.get(network_type) + host_endpoint = driver.obj.get_endpoint_by_host(host_id) + if host_endpoint: + local_ip = host_endpoint['ip_address'] + else: + local_ip = None + else: + local_ip = host_id + + return host_id, local_ip, network_type, segment_id, mac_address + + def _get_ingress_egress_tap_ports(self, port_pair): + ingress_shadow_port_id = port_pair.get('ingress') + egress_shadow_port_id = port_pair.get('egress') + + core_plugin = manager.NeutronManager.get_plugin() + in_shadow_pd = core_plugin.get_port(self.admin_context, + ingress_shadow_port_id) + eg_shadow_pd = core_plugin.get_port(self.admin_context, + egress_shadow_port_id) + return in_shadow_pd['device_owner'], eg_shadow_pd['device_owner'] + + def _get_shadow_port_segment_id(self, port): + core_plugin = manager.NeutronManager.get_plugin() + port_detail = core_plugin.get_port(self.admin_context, port) + network_id = port_detail['network_id'] + network_info = core_plugin.get_network( + self.admin_context, network_id) + network_type = network_info['provider:network_type'] + segment_id = network_info['provider:segmentation_id'] + + return network_type, segment_id + + @log_helpers.log_method_call + def _create_port_detail(self, port_pair): + # since first node may not assign the ingress port, and last node may + # not assign the egress port. we use one of the + # port as the key to get the SF information. + port = None + if port_pair.get('ingress', None): + port = port_pair['ingress'] + elif port_pair.get('egress', None): + port = port_pair['egress'] + + host_id, local_endpoint, network_type, segment_id, mac_address = ( + self._get_portpair_detail_info(port)) + + ingress, egress = port_pair.get('ingress'), port_pair.get('egress') + + port_detail = { + 'ingress': ingress, + 'egress': egress, + 'tenant_id': port_pair['tenant_id'], + 'host_id': host_id, + 'segment_id': segment_id, + 'network_type': network_type, + 'local_endpoint': local_endpoint, + 'mac_address': mac_address + } + r = self.create_port_detail(port_detail) + LOG.debug('create port detail: %s', r) + return r diff --git a/networking_sfc/services/sfc/drivers/ovs/driver.py b/networking_sfc/services/sfc/drivers/ovs/driver.py index 74d82ba3..8f770abf 100644 --- a/networking_sfc/services/sfc/drivers/ovs/driver.py +++ b/networking_sfc/services/sfc/drivers/ovs/driver.py @@ -22,7 +22,6 @@ from neutron.common import rpc as n_rpc from neutron import context as n_context from neutron.db import api as db_api -from neutron.db import segments_db as seg_db from neutron import manager from neutron.plugins.common import constants as np_const @@ -262,57 +261,31 @@ def _add_flowclassifier_port_assoc(self, fc_ids, tenant_id, src_node): for fc in self._get_fcs_by_ids(fc_ids): need_assoc = True - src_pd_filter = dst_pd_filter = None - - if fc['logical_source_port']: - # lookup the source port - src_pd_filter = dict( - egress=fc['logical_source_port'], - tenant_id=tenant_id - ) - if fc['logical_destination_port']: - # lookup the source port - dst_pd_filter = dict( - ingress=fc['logical_destination_port'], - tenant_id=tenant_id - ) + # lookup the source port + src_pd_filter = dict( + egress=fc['logical_source_port'], + tenant_id=tenant_id + ) src_pd = self.get_port_detail_by_filter(src_pd_filter) - dst_pd = self.get_port_detail_by_filter(dst_pd_filter) - - new_src_pd = new_dst_pd = '' - if not (src_pd and dst_pd): - if not src_pd: - # Create source port detail - new_src_pd = self._create_port_detail(src_pd_filter) - LOG.debug('create src port detail: %s', new_src_pd) - if not dst_pd: - # Create destination port detail - new_dst_pd = self._create_port_detail(dst_pd_filter) - LOG.debug('create dst port detail: %s', new_dst_pd) + + if not src_pd: + # Create source port detail + src_pd = self._create_port_detail(src_pd_filter) + LOG.debug('create src port detail: %s', src_pd) else: for path_node in src_pd['path_nodes']: if path_node['pathnode_id'] == src_node['id']: need_assoc = False if need_assoc: # Create associate relationship - if new_src_pd: - assco_args = { - 'portpair_id': new_src_pd['id'], - 'pathnode_id': src_node['id'], - 'weight': 1, - } - sna = self.create_pathport_assoc(assco_args) - LOG.debug('create assoc src port with node: %s', sna) - src_node['portpair_details'].append(new_src_pd['id']) - if new_dst_pd: - assco_args = { - 'portpair_id': new_dst_pd['id'], - 'pathnode_id': src_node['id'], - 'weight': 1, - } - sna = self.create_pathport_assoc(assco_args) - LOG.debug('create assoc src port with node: %s', sna) - src_node['portpair_details'].append(new_dst_pd['id']) + assco_args = { + 'portpair_id': src_pd['id'], + 'pathnode_id': src_node['id'], + 'weight': 1, + } + sna = self.create_pathport_assoc(assco_args) + LOG.debug('create assoc src port with node: %s', sna) + src_node['portpair_details'].append(src_pd['id']) def _remove_flowclassifier_port_assoc(self, fc_ids, tenant_id, src_node): @@ -336,64 +309,9 @@ def _remove_flowclassifier_port_assoc(self, fc_ids, tenant_id, if len(pd['path_nodes']) == 1: self.delete_port_detail(pd['id']) - def _create_src_and_dest_nodes(self, port_chain, next_group_intid, - next_group_members, path_nodes): - path_id = port_chain['chain_id'] - port_pair_groups = port_chain['port_pair_groups'] - sf_path_length = len(port_pair_groups) - - # Create a head node object for port chain - src_args = {'tenant_id': port_chain['tenant_id'], - 'node_type': ovs_const.SRC_NODE, - 'nsp': path_id, - 'nsi': 0xff, - 'portchain_id': port_chain['id'], - 'status': ovs_const.STATUS_BUILDING, - 'next_group_id': next_group_intid, - 'next_hop': jsonutils.dumps(next_group_members), - } - src_node = self.create_path_node(src_args) - LOG.debug('create src node: %s', src_node) - path_nodes.append(src_node) - - # Create a destination node object for port chain - dst_args = { - 'tenant_id': port_chain['tenant_id'], - 'node_type': ovs_const.DST_NODE, - 'nsp': path_id, - 'nsi': 0xff - sf_path_length - 1, - 'portchain_id': port_chain['id'], - 'status': ovs_const.STATUS_BUILDING, - 'next_group_id': None, - 'next_hop': None - } - dst_node = self.create_path_node(dst_args) - LOG.debug('create dst node: %s', dst_node) - path_nodes.append(dst_node) - - return src_node - - def _check_if_bi_node(self, src_node): - ''' We assume that if the flow classifiers associated with - a port chain contain both LSP and LDP, we treat it as a bi-directional - chain. LSP and LDP can be part of same or different flow - classifier(s) ''' - - is_lsp = False - is_ldp = False - portpair_details = src_node['portpair_details'] - for each in portpair_details: - port_detail = self.get_port_detail_by_filter(dict(id=each)) - if port_detail['egress']: - is_lsp = True - else: - is_ldp = True - if is_lsp and is_ldp: - return True - return False - @log_helpers.log_method_call def _create_portchain_path(self, context, port_chain): + src_node, src_pd, dst_node, dst_pd = (({}, ) * 4) path_nodes = [] # Create an assoc object for chain_id and path_id # context = context._plugin_context @@ -409,12 +327,8 @@ def _create_portchain_path(self, context, port_chain): # Detect cross-subnet transit # Compare subnets for logical source ports # and first PPG ingress ports - '''for fc in self._get_fcs_by_ids(port_chain['flow_classifiers']): - if fc['logical_source_port']: - subnet1 = self._get_subnet_by_port(fc['logical_source_port']) - else: - subnet1 = self._get_subnet_by_port(fc[ - 'logical_destination_port']) + for fc in self._get_fcs_by_ids(port_chain['flow_classifiers']): + subnet1 = self._get_subnet_by_port(fc['logical_source_port']) cidr1 = subnet1['cidr'] ppg = context._plugin.get_port_pair_group(context._plugin_context, port_pair_groups[0]) @@ -430,7 +344,7 @@ def _create_portchain_path(self, context, port_chain): if cidr1 != cidr2: LOG.error(_LE('Cross-subnet chain not supported')) raise exc.SfcDriverError() - return None''' + return None # Compare subnets for PPG egress ports # and next PPG ingress ports @@ -466,9 +380,34 @@ def _create_portchain_path(self, context, port_chain): next_group_intid, next_group_members = self._get_portgroup_members( context, port_chain['port_pair_groups'][0]) - src_node = self._create_src_and_dest_nodes( - port_chain, next_group_intid, - next_group_members, path_nodes) + # Create a head node object for port chain + src_args = {'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.SRC_NODE, + 'nsp': path_id, + 'nsi': 0xff, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': next_group_intid, + 'next_hop': jsonutils.dumps(next_group_members), + } + src_node = self.create_path_node(src_args) + LOG.debug('create src node: %s', src_node) + path_nodes.append(src_node) + + # Create a destination node object for port chain + dst_args = { + 'tenant_id': port_chain['tenant_id'], + 'node_type': ovs_const.DST_NODE, + 'nsp': path_id, + 'nsi': 0xff - sf_path_length - 1, + 'portchain_id': port_chain['id'], + 'status': ovs_const.STATUS_BUILDING, + 'next_group_id': None, + 'next_hop': None + } + dst_node = self.create_path_node(dst_args) + LOG.debug('create dst node: %s', dst_node) + path_nodes.append(dst_node) self._add_flowclassifier_port_assoc( port_chain['flow_classifiers'], @@ -476,13 +415,6 @@ def _create_portchain_path(self, context, port_chain): src_node ) - is_bi_node = self._check_if_bi_node(src_node) - - if is_bi_node: - path_nodes[0].update(dict(reverse_path=True)) - else: - path_nodes[0].update(dict(reverse_path=False)) - for i in range(sf_path_length): cur_group_members = next_group_members # next_group for next hop @@ -511,12 +443,6 @@ def _create_portchain_path(self, context, port_chain): } sf_node = self.create_path_node(node_args) LOG.debug('chain path node: %s', sf_node) - - # If Src Node is bi, then SF node shall naturally be bi. - if is_bi_node: - sf_node.update(dict(reverse_path=True)) - else: - sf_node.update(dict(reverse_path=False)) # Create the assocation objects that combine the pathnode_id with # the ingress of the port_pairs in the current group # when port_group does not reach tail @@ -541,12 +467,6 @@ def _delete_path_node_port_flowrule(self, node, port, fc_ids): None, fc_ids) - if flow_rule['reverse_path']: - if (flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule[ - 'ingress']): - flow_rule = self._reverse_flow_rules(flow_rule) - - LOG.info("ZZZ FLOW RULES %r" % flow_rule) self.ovs_driver_rpc.ask_agent_to_delete_flow_rules( self.admin_context, flow_rule) @@ -559,9 +479,6 @@ def _delete_path_node_flowrule(self, node, fc_ids): for each in node['portpair_details']: port = self.get_port_detail_by_filter(dict(id=each)) if port: - if node['node_type'] == ovs_const.SF_NODE: - _, egress = self._get_ingress_egress_tap_ports(port) - port.update({'egress': egress}) self._delete_path_node_port_flowrule( node, port, fc_ids) @@ -570,22 +487,10 @@ def _delete_portchain_path(self, context, port_chain): pds = self.get_path_nodes_by_filter( dict(portchain_id=port_chain['id'])) src_node = None - is_bi_node = True if pds: for pd in pds: - ''' We cannot assume that Src node always comes first. - # If Src Node is bi, then SF node shall naturally be bi. Here, - # we assume that Src node will be the first in 'pds'. - if self._check_if_bi_node(pd): - is_bi_node = True''' - if is_bi_node: - pd.update(dict(reverse_path=True)) - else: - pd.update(dict(reverse_path=False)) - if pd['node_type'] == ovs_const.SRC_NODE: src_node = pd - self._delete_path_node_flowrule( pd, port_chain['flow_classifiers'] @@ -614,7 +519,6 @@ def _update_path_node_next_hops(self, flow_rule): dict(id=member['portpair_id'])) if not port_detail or not port_detail['host_id']: continue - detail['local_endpoint'] = port_detail['local_endpoint'] detail['weight'] = member['weight'] detail['mac_address'] = port_detail['mac_address'] @@ -664,7 +568,6 @@ def _filter_flow_classifiers(self, flow_rule, fc_ids): """ fc_return = [] - core_plugin = manager.NeutronManager.get_plugin() if not fc_ids: return fc_return @@ -676,51 +579,16 @@ def _filter_flow_classifiers(self, flow_rule, fc_ids): new_fc.pop('tenant_id') new_fc.pop('description') - lsp = new_fc.get('logical_source_port') - ldp = new_fc.get('logical_destination_port') - if lsp: - port_detail = core_plugin.get_port(self.admin_context, lsp) - new_fc['lsp_mac_address'] = port_detail['mac_address'] - new_fc['source_ip_prefix'] = port_detail[ - 'fixed_ips'][0]['ip_address'] - if ldp: - port_detail = core_plugin.get_port(self.admin_context, ldp) - new_fc['ldp_mac_address'] = port_detail['mac_address'] - new_fc['destination_ip_prefix'] = port_detail[ - 'fixed_ips'][0]['ip_address'] - if ( - flow_rule['node_type'] in [ovs_const.SRC_NODE] and + flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule['egress'] == fc['logical_source_port'] - ) or ( - flow_rule['node_type'] in [ovs_const.SRC_NODE] and - flow_rule['ingress'] == fc['logical_destination_port'] ): fc_return.append(new_fc) - elif flow_rule['node_type'] in [ovs_const.SF_NODE]: + elif flow_rule['node_type'] == ovs_const.SF_NODE: fc_return.append(new_fc) return fc_return - def _reverse_flow_rules(self, flowrule): - - def _reverse_fcs(op): - for fc in flowrule[op]: - fc['logical_destination_port'], fc['logical_source_port'] = ( - fc['logical_source_port'], fc['logical_destination_port']) - fc['ldp_mac_address'], fc['lsp_mac_address'] = ( - fc['lsp_mac_address'], fc['ldp_mac_address']) - fc['destination_ip_prefix'], fc['source_ip_prefix'] = ( - fc['source_ip_prefix'], fc['destination_ip_prefix']) - - for op in ['add_fcs', 'del_fcs']: - _reverse_fcs(op) - - flowrule['ingress'], flowrule['egress'] = ( - flowrule['egress'], flowrule['ingress']) - - return flowrule - def _update_path_node_port_flowrules(self, node, port, add_fc_ids=None, del_fc_ids=None): # if this port is not binding, don't to generate flow rule @@ -733,12 +601,6 @@ def _update_path_node_port_flowrules(self, node, port, add_fc_ids, del_fc_ids) - if flow_rule['reverse_path']: - if (flow_rule['node_type'] == ovs_const.SRC_NODE and flow_rule[ - 'ingress']): - flow_rule = self._reverse_flow_rules(flow_rule) - - LOG.info("YYY FLOW RULES %r" % flow_rule) self.ovs_driver_rpc.ask_agent_to_update_flow_rules( self.admin_context, flow_rule) @@ -751,11 +613,7 @@ def _update_path_node_flowrules(self, node, return for each in node['portpair_details']: port = self.get_port_detail_by_filter(dict(id=each)) - if port: - if node['node_type'] == ovs_const.SF_NODE: - _, egress = self._get_ingress_egress_tap_ports(port) - port.update({'egress': egress}) self._update_path_node_port_flowrules( node, port, add_fc_ids, del_fc_ids) @@ -791,7 +649,6 @@ def _get_fcs_by_ids(self, fc_ids): def create_port_chain(self, context): port_chain = context.current path_nodes = self._create_portchain_path(context, port_chain) - LOG.info("XXX PATH NODES %r" % path_nodes) self._update_path_nodes( path_nodes, port_chain['flow_classifiers'], @@ -940,58 +797,24 @@ def _get_portpair_detail_info(self, portpair_id): network_type = network_info['provider:network_type'] segment_id = network_info['provider:segmentation_id'] - if network_type not in [np_const.TYPE_VXLAN, np_const.TYPE_VLAN]: - LOG.warning(_LW("Currently only support vxlan and vlan networks")) + if network_type != np_const.TYPE_VXLAN: + LOG.warning(_LW("Currently only support vxlan network")) return ((None, ) * 5) elif not host_id: LOG.warning(_LW("This port has not been binding")) return ((None, ) * 5) else: - if network_type == np_const.TYPE_VXLAN: - driver = core_plugin.type_manager.drivers.get(network_type) - host_endpoint = driver.obj.get_endpoint_by_host(host_id) - if host_endpoint: - local_ip = host_endpoint['ip_address'] - else: - local_ip = None + driver = core_plugin.type_manager.drivers.get(network_type) + host_endpoint = driver.obj.get_endpoint_by_host(host_id) + if host_endpoint: + local_ip = host_endpoint['ip_address'] else: - local_ip = host_id - - return (host_id, local_ip, network_type, - segment_id, mac_address, network_id) + local_ip = None - def _get_ingress_egress_tap_ports(self, port_pair): - ingress_shadow_port_id = port_pair.get('ingress') - egress_shadow_port_id = port_pair.get('egress') - - core_plugin = manager.NeutronManager.get_plugin() - in_shadow_pd = core_plugin.get_port(self.admin_context, - ingress_shadow_port_id) - eg_shadow_pd = core_plugin.get_port(self.admin_context, - egress_shadow_port_id) - return in_shadow_pd['device_owner'], eg_shadow_pd['device_owner'] - - def _get_shadow_port_segment_id(self, port): - core_plugin = manager.NeutronManager.get_plugin() - port_detail = core_plugin.get_port(self.admin_context, port) - network_id = port_detail['network_id'] - network_info = core_plugin.get_network( - self.admin_context, network_id) - network_type = network_info['provider:network_type'] - segment_id = network_info['provider:segmentation_id'] - - return network_type, segment_id - - def _get_segmentation_id(self, network_id): - session = db_api.get_session() - nw_seg_data = seg_db.get_network_segments(session, network_id) - - for each in nw_seg_data: - if each['network_type'] == np_const.TYPE_VLAN: - return each['segmentation_id'] + return host_id, local_ip, network_type, segment_id, mac_address @log_helpers.log_method_call - def _create_port_detail(self, port_pair, is_sf=False): + def _create_port_detail(self, port_pair): # since first node may not assign the ingress port, and last node may # not assign the egress port. we use one of the # port as the key to get the SF information. @@ -1001,29 +824,11 @@ def _create_port_detail(self, port_pair, is_sf=False): elif port_pair.get('egress', None): port = port_pair['egress'] - (host_id, local_endpoint, network_type, - segment_id, mac_address, network_id) = ( + host_id, local_endpoint, network_type, segment_id, mac_address = ( self._get_portpair_detail_info(port)) - - if network_type == np_const.TYPE_VXLAN: - segment_id = self._get_segmentation_id(network_id) - - '''if not is_sf: - host_id, local_endpoint, network_type, segment_id, mac_address = ( - self._get_portpair_detail_info(port)) - - if is_sf: - ingress, _ = self._get_ingress_egress_tap_ports(port_pair) - host_id, local_endpoint, network_type, segment_id, mac_address = ( - self._get_portpair_detail_info(ingress)) - network_type, segment_id = self._get_shadow_port_segment_id(port) - ''' - - ingress, egress = port_pair.get('ingress'), port_pair.get('egress') - port_detail = { - 'ingress': ingress, - 'egress': egress, + 'ingress': port_pair.get('ingress', None), + 'egress': port_pair.get('egress', None), 'tenant_id': port_pair['tenant_id'], 'host_id': host_id, 'segment_id': segment_id, @@ -1038,7 +843,7 @@ def _create_port_detail(self, port_pair, is_sf=False): @log_helpers.log_method_call def create_port_pair(self, context): port_pair = context.current - self._create_port_detail(port_pair, is_sf=True) + self._create_port_detail(port_pair) @log_helpers.log_method_call def delete_port_pair(self, context): diff --git a/setup.cfg b/setup.cfg index 1dd9aa50..4d720942 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,13 +63,16 @@ neutron.service_plugins = networking_sfc.sfc.drivers = dummy = networking_sfc.services.sfc.drivers.dummy.dummy:DummyDriver ovs = networking_sfc.services.sfc.drivers.ovs.driver:OVSSfcDriver + oc = networking_sfc.services.sfc.drivers.oc.driver:OCSfcDriver networking_sfc.flowclassifier.drivers = dummy = networking_sfc.services.flowclassifier.drivers.dummy.dummy:DummyDriver ovs = networking_sfc.services.flowclassifier.drivers.ovs.driver:OVSFlowClassifierDriver + oc = networking_sfc.services.flowclassifier.drivers.oc.driver:OCFlowClassifierDriver neutron.agent.l2.extensions = sfc = networking_sfc.services.sfc.agent.extensions.sfc:SfcAgentExtension networking_sfc.sfc.agent_drivers = - ovs = networking_sfc.services.sfc.agent.extensions.openvswitch.sfc_driver:SfcOVSAgentDriver + #ovs = networking_sfc.services.sfc.agent.extensions.openvswitch.sfc_driver:SfcOVSAgentDriver + ovs = networking_sfc.services.sfc.agent.extensions.oc.sfc_driver:SfcOCAgentDriver tempest.test_plugins = networking-sfc = networking_sfc.tests.tempest_plugin.plugin:NetworkingSfcPlugin oslo.config.opts = From dd47adccf12da92fab17c2b733074cdeb610a5bf Mon Sep 17 00:00:00 2001 From: dpaks Date: Tue, 9 May 2017 21:58:26 +0530 Subject: [PATCH 10/16] added missing things from sfc tap branch, removed host name dependency --- .../services/sfc/agent/extensions/oc/sfc_driver.py | 10 +++------- networking_sfc/services/sfc/drivers/oc/driver.py | 11 ----------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py index efb9dcbc..24fb41fd 100644 --- a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py @@ -24,7 +24,6 @@ from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager from networking_sfc._i18n import _LE -from networking_sfc.services.sfc.agent.extensions import sfc from networking_sfc.services.sfc.common import ovs_ext_lib from networking_sfc.services.sfc.agent.extensions.openvswitch import sfc_driver from networking_sfc.services.sfc.drivers.ovs import constants @@ -81,7 +80,6 @@ class SfcOCAgentDriver(sfc_driver.SfcOVSAgentDriver): def __init__(self): super(SfcOCAgentDriver, self).__init__() self.ovs_sfc_dvr = sfc_driver.SfcOVSAgentDriver() - self.local_host = None def consume_api(self, agent_api): self.agent_api = agent_api @@ -92,11 +90,11 @@ def initialize(self): self.br_int.set_protocols(SfcOCAgentDriver.REQUIRED_PROTOCOLS) self.local_ip = cfg.CONF.OVS.local_ip + self.phy_patch_ofport = self.br_int.get_port_ofport( + cfg.CONF.OVS.phy_patch_ofport) self.vlan_manager = vlanmanager.LocalVlanManager() self._clear_sfc_flow_on_int_br() - self.phy_patch_ofport = self.br_int.get_port_ofport( - cfg.CONF.OVS.phy_patch_ofport) def update_flow_rules(self, flowrule, flowrule_status): try: @@ -236,7 +234,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): subnet_actions_list = [] priority = 30 - if item['local_endpoint'] == self.local_host: + if item['local_endpoint'] == flowrule['host']: subnet_actions = ( "mod_vlan_vid:%d, resubmit(,%d)" % (global_vlan_tag, INGRESS_TABLE)) @@ -393,8 +391,6 @@ def _setup_destination_based_flows(self, flowrule, # B5. At ingress of SF, if dl_dst belongs to SF and nw_dst # belongs to Dest VM, output to ingress port of SF. for fc in flow_classifier_list: - dst_ip = fc['destination_ip_prefix'] - match_info = dict(dl_type=0x0800, dl_vlan=global_vlan_tag, dl_dst=ingress_mac) diff --git a/networking_sfc/services/sfc/drivers/oc/driver.py b/networking_sfc/services/sfc/drivers/oc/driver.py index 1b8386e7..31ad46b1 100644 --- a/networking_sfc/services/sfc/drivers/oc/driver.py +++ b/networking_sfc/services/sfc/drivers/oc/driver.py @@ -524,17 +524,6 @@ def _get_ingress_egress_tap_ports(self, port_pair): egress_shadow_port_id) return in_shadow_pd['device_owner'], eg_shadow_pd['device_owner'] - def _get_shadow_port_segment_id(self, port): - core_plugin = manager.NeutronManager.get_plugin() - port_detail = core_plugin.get_port(self.admin_context, port) - network_id = port_detail['network_id'] - network_info = core_plugin.get_network( - self.admin_context, network_id) - network_type = network_info['provider:network_type'] - segment_id = network_info['provider:segmentation_id'] - - return network_type, segment_id - @log_helpers.log_method_call def _create_port_detail(self, port_pair): # since first node may not assign the ingress port, and last node may From 65fe9612fbd5f2f4a82b8dd5b25a7a83bfeee9ec Mon Sep 17 00:00:00 2001 From: dpaks Date: Wed, 10 May 2017 15:36:15 +0530 Subject: [PATCH 11/16] removed usage of device owner for shadow ports, using device id with parent port, demo script changes --- demo.sh | 31 ++++++------------- networking_sfc/db/sfc_db.py | 4 +-- .../services/sfc/drivers/oc/driver.py | 2 +- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/demo.sh b/demo.sh index c3243af1..d8b6f42d 100644 --- a/demo.sh +++ b/demo.sh @@ -73,18 +73,15 @@ if [ "$operation" == "create" ]; then openstack network trunk create --parent-port $p1_id trunk1 echo "Launching VNF on Node $sf_compute..." - openstack server delete VNF nova boot --image $sf_image --flavor 3 --nic port-id=$p1_id\ --nic port-id=$p2_id --availability-zone nova:$sf_compute VNF sleep 5 - echo "Launching Attacker on Node $c1_compute..." - openstack server delete Attacker - nova boot --image $client_image --flavor 2 --nic port-id=$c1_id --availability-zone nova:$c1_compute Attacker - echo "Launching InspectedVM on Node $c2_compute..." - openstack server delete InspectedVM - nova boot --image $client_image --flavor 2 --nic port-id=$c2_id --availability-zone nova:$c2_compute InspectedVM + echo "Launching Client on Node $c1_compute..." + nova boot --image $client_image --flavor 2 --nic port-id=$c1_id --availability-zone nova:$c1_compute Client + echo "Launching Server on Node $c2_compute..." + nova boot --image $client_image --flavor 2 --nic port-id=$c2_id --availability-zone nova:$c2_compute Server sleep 5 @@ -94,12 +91,11 @@ if [ "$operation" == "create" ]; then s1_id=`openstack port list -f value | grep " s1 " | awk '{print $1}'` seg_id=`openstack network show inspected_net | grep segmentation_id | awk '{print $4}'` - openstack port create --network inspected_net --device $vnf_id\ - --device-owner $p2_id --mac-address $p2_mac s2 + openstack port create --network inspected_net --device $p2_id --mac-address $p2_mac s2 openstack network trunk set --subport port=$s1_id,segmentation-type=vlan,segmentation-id=$seg_id trunk1 - openstack port set --device $vnf_id s1 + openstack port set --device $p1_id s1 sleep 3 @@ -123,10 +119,11 @@ else openstack port pair group delete ppg1 openstack port pair delete pp1 - openstack server delete Attacker InspectedVM VNF - s1_id=`openstack port list -f value | grep " s1 " | awk '{print $1}'` openstack network trunk unset --subport $s1_id trunk1 + + openstack server delete Client Server VNF + openstack network trunk delete trunk1 openstack port delete s1 s2 c1 c2 p1 p2 @@ -136,13 +133,3 @@ else openstack router delete sfc-router openstack network delete inspected_net inspection_net fi - - - - - - - - - - \ No newline at end of file diff --git a/networking_sfc/db/sfc_db.py b/networking_sfc/db/sfc_db.py index 04d7848a..ee628e39 100644 --- a/networking_sfc/db/sfc_db.py +++ b/networking_sfc/db/sfc_db.py @@ -431,10 +431,10 @@ def _validate_port_pair_ingress_egress(self, ingress, egress): raise ext_sfc.PortPairEgressNoHost( egress=egress['id'] ) - if ingress['device_id'] != egress['device_id']: + '''if ingress['device_id'] != egress['device_id']: raise ext_sfc.PortPairIngressEgressDifferentHost( ingress=ingress['id'], - egress=egress['id']) + egress=egress['id'])''' @log_helpers.log_method_call def create_port_pair(self, context, port_pair): diff --git a/networking_sfc/services/sfc/drivers/oc/driver.py b/networking_sfc/services/sfc/drivers/oc/driver.py index 31ad46b1..4fc2a075 100644 --- a/networking_sfc/services/sfc/drivers/oc/driver.py +++ b/networking_sfc/services/sfc/drivers/oc/driver.py @@ -522,7 +522,7 @@ def _get_ingress_egress_tap_ports(self, port_pair): ingress_shadow_port_id) eg_shadow_pd = core_plugin.get_port(self.admin_context, egress_shadow_port_id) - return in_shadow_pd['device_owner'], eg_shadow_pd['device_owner'] + return in_shadow_pd['device_id'], eg_shadow_pd['device_id'] @log_helpers.log_method_call def _create_port_detail(self, port_pair): From e28c3bd0ec50c255e3b16b67853a27f2b070f90d Mon Sep 17 00:00:00 2001 From: dpaks Date: Thu, 11 May 2017 11:29:27 +0530 Subject: [PATCH 12/16] helps phy swtch learn vnf ingress mac --- .../sfc/agent/extensions/oc/sfc_driver.py | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py index 24fb41fd..3eb6010b 100644 --- a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py @@ -303,7 +303,6 @@ def _setup_source_based_flows( next_hops = flowrule.get('next_hops') if not (group_id and next_hops): local_vlan_tag = self._get_vlan_by_port(flowrule['ingress']) - egress_mac = egress_port.vif_mac # B6. For packets coming out of SF, we resubmit to table 5. match_info = dict(dl_type=0x0800, **inport_match) actions = ("resubmit(,%s)" % ACROSS_SUBNET_TABLE) @@ -326,7 +325,7 @@ def _setup_source_based_flows( else: actions = ("mod_vlan_vid:%s, mod_dl_src:%s, " "mod_dl_dst:%s, output:%s" % ( - (local_vlan_tag, egress_mac, + (local_vlan_tag, ingress_mac, ldp_mac, self.phy_patch_ofport))) match_info = dict(nw_dst=fc['destination_ip_prefix'], @@ -390,24 +389,23 @@ def _setup_destination_based_flows(self, flowrule, if not (group_id and next_hops): # B5. At ingress of SF, if dl_dst belongs to SF and nw_dst # belongs to Dest VM, output to ingress port of SF. - for fc in flow_classifier_list: - match_info = dict(dl_type=0x0800, - dl_vlan=global_vlan_tag, - dl_dst=ingress_mac) - actions = ("strip_vlan, output:%s" % (ingress_ofport)) + match_info = dict(dl_type=0x0800, + dl_vlan=global_vlan_tag, + dl_dst=ingress_mac) + actions = ("strip_vlan, output:%s" % (ingress_ofport)) - self._update_flows(INGRESS_TABLE, 60, - match_info, actions, add_flow) + self._update_flows(INGRESS_TABLE, 60, + match_info, actions, add_flow) - # B4. At ingress of SF, if dest mac matches with SF ingress, - # vlan matches, then resubmit to 10. - # This is per ldp because ldps can have different vlan tags. - match_info = dict( - dl_type=0x0800, in_port=self.phy_patch_ofport, - dl_vlan=global_vlan_tag, dl_dst=ingress_mac) - actions = ("resubmit(,%s)" % INGRESS_TABLE) - self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, - match_info, actions, add_flow) + # B4. At ingress of SF, if dest mac matches with SF ingress, + # vlan matches, then resubmit to 10. + # This is per ldp because ldps can have different vlan tags. + match_info = dict( + dl_type=0x0800, in_port=self.phy_patch_ofport, + dl_vlan=global_vlan_tag, dl_dst=ingress_mac) + actions = ("resubmit(,%s)" % INGRESS_TABLE) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_info, actions, add_flow) return # B9. Match IP packets with vlan and source IP. Actions will be to From 2542eac7f4cc68900d0125d02a7f917ae43ee55a Mon Sep 17 00:00:00 2001 From: dpaks Date: Thu, 11 May 2017 17:03:12 +0530 Subject: [PATCH 13/16] bug fix, add nw_src match in dest egress --- .../sfc/agent/extensions/oc/sfc_driver.py | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py index 3eb6010b..ab501d8f 100644 --- a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py @@ -229,7 +229,7 @@ def _setup_egress_flow_rules(self, flowrule, match_inport=True): ) ) buckets.append(bucket) - # A3 In table 5, add MPLS header and send to either patch port + # A3 In table 5, send to either patch port # or table 10 for remote and local node respectively. subnet_actions_list = [] @@ -339,13 +339,14 @@ def _setup_source_based_flows( # A1. Flow inserted at LSP egress. Matches on ip, in_port and LDP IP. # Action is redirect to group. - ldp_mac = flow_classifier_list[0]['ldp_mac_address'] - actions = ("mod_vlan_vid:%s, group:%d" % ( - local_vlan_tag, group_id)) - match_info = dict(inport_match, **{'dl_dst': ldp_mac, - 'dl_type': '0x0800'}) - self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, - match_info, actions, add_flow) + for fc in flow_classifier_list: + ldp_mac = fc['ldp_mac_address'] + actions = ("mod_vlan_vid:%s, group:%d" % ( + local_vlan_tag, group_id)) + match_info = dict(inport_match, **{'dl_dst': ldp_mac, + 'dl_type': '0x0800'}) + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_info, actions, add_flow) def _get_port_info(self, port_id, info_type): ''' Returns specific port info @@ -410,31 +411,33 @@ def _setup_destination_based_flows(self, flowrule, # B9. Match IP packets with vlan and source IP. Actions will be to # strip vlan and modify src MAC and output to Dest VM port. - nw_src = flow_classifier_list[0]['source_ip_prefix'] - - src_port_mac = flow_classifier_list[0]['lsp_mac_address'] - actions = ('strip_vlan, mod_dl_src:%s, output:%s' % ( - src_port_mac, ingress_ofport)) - - match_field = dict( - dl_type=0x0800, - dl_vlan=global_vlan_tag, - dl_dst=ingress_mac, - nw_src=nw_src) - - self._update_flows(10, priority, - match_field, actions, add_flow) - - # B8. At ingress of dest, match ip packet with vlan tag and dest - # MAC address. Action will be to resubmit to 10. - match_field = dict( - dl_type=0x0800, - dl_vlan=global_vlan_tag, - dl_dst=ingress_mac) - actions = ("resubmit(,%s)" % 10) - - self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, - match_field, actions, add_flow) + for fc in flow_classifier_list: + nw_src = fc['source_ip_prefix'] + + src_port_mac = fc['lsp_mac_address'] + actions = ('strip_vlan, mod_dl_src:%s, output:%s' % ( + src_port_mac, ingress_ofport)) + + match_field = dict( + dl_type=0x0800, + dl_vlan=global_vlan_tag, + dl_dst=ingress_mac, + nw_src=nw_src) + + self._update_flows(10, priority, + match_field, actions, add_flow) + + # B8. At ingress of dest, match ip packet with vlan tag and dest + # MAC address. Action will be to resubmit to 10. + match_field = dict( + dl_type=0x0800, + dl_vlan=global_vlan_tag, + dl_dst=ingress_mac, + nw_src=nw_src) + actions = ("resubmit(,%s)" % 10) + + self._update_flows(ovs_consts.LOCAL_SWITCHING, priority, + match_field, actions, add_flow) def _setup_ingress_flow_rules(self, flowrule): flow_classifier_list = flowrule['add_fcs'] From 0fee8738aadcaa2cdc18dd255a428326692cf571 Mon Sep 17 00:00:00 2001 From: dpaks Date: Fri, 12 May 2017 14:01:30 +0530 Subject: [PATCH 14/16] adding script and fix for removing stale flow A3 --- apply_sfc_patch.sh | 112 ++++++++++++++++++ .../sfc/agent/extensions/oc/sfc_driver.py | 9 +- 2 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 apply_sfc_patch.sh diff --git a/apply_sfc_patch.sh b/apply_sfc_patch.sh new file mode 100644 index 00000000..a43f562e --- /dev/null +++ b/apply_sfc_patch.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +VERSION="3.0.1" + +PATCH_PATH=$1 +PATCH_PORT=$2 + +Usage () { + echo -e "\n\nUsage: apply_sfc_patch.sh \n" + echo -e "\nThe patch SHOULD belong to version $VERSION and should be " + echo -e "applied ONLY on top of sfc rpm with version $VERSION.\n" +} + +# diff -Nur -x "*.py?" networking_sfc/db/ Projects/networking-sfc/networking_sfc/db/ > sfc_db-3.0.1.patch +# diff -Nur -x "*.py?" networking_sfc/services/sfc/agent/extensions/oc Projects/networking-sfc/networking_sfc/services/sfc/agent/extensions/oc > sfc_agent-3.0.1.patch +# + +function backup_files_to_be_modifed(){ + echo "Backing up files that are to be modified while patching with $1." + if [[ $1 == "sfc_db.patch" ]]; then + cp $PYTHON_MODULE_PATH/networking_sfc/db/sfc_db.py $PYTHON_MODULE_PATH/networking_sfc/db/sfc_db.py.bk + cp $PYTHON_MODULE_PATH/networking_sfc/db/flowclassifier_db.py $PYTHON_MODULE_PATH/networking_sfc/db/flowclassifier_db.py.bk + elif [[ $1 == "sfc_agent.patch" ]]; then + cp $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py.bk + elif [[ $1 == "sfc_plugin.patch" ]]; then + cp $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/driver.py $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/driver.py.bk + else + : + fi +} + +function apply_patches(){ + cd $PYTHON_MODULE_PATH + for patch in ${PATCH_ARRAY[@]} + do + if [[ $patch != *"$VERSION"* ]]; then + echo -e "\nThe patches must be of version $VERSION." + exit 1 + fi + test_patch=`patch -R -p0 --dry-run --silent > /dev/null < $patch` + patch_status=`echo $?` + if [ $patch_status == 0 ]; then + echo "$patch is already applied so skipping." + else + backup_files_to_be_modifed $patch + echo "Applying patch $patch ..." + patch -p0 < $patch + sleep 2 + fi + done +} + +function check_for_sfc(){ + sfc_rpm="$(rpm -qa | grep networking-sfc)" + if [[ -z $sfc_rpm ]]; then + echo -e "\nSFC RPM is missing in this node. This script works ONLY if a valid SFC rpm is installed. Aborting..." + exit 1 + fi + + if [[ $sfc_rpm != *"$VERSION"* ]]; then + echo -e "\nThis script works ONLY on SFC RPM installed with version $VERSION." + exit 1 + fi +} + +function copy_sfc_patches(){ + cp $PATCH_PATH/sfc/sfc_plugin.patch $PYTHON_MODULE_PATH/. + cp $PATCH_PATH/sfc/sfc_agent.patch $PYTHON_MODULE_PATH/. + cp $PATCH_PATH/sfc/sfc_db.patch $PYTHON_MODULE_PATH/. +} + +function configure_sfc(){ + NEUTRON_CONF="/etc/neutron/neutron.conf" + OVS_INI="/etc/neutron/plugins/ml2/openvswitch_agent.ini" + EGG_FILE="/usr/lib/python2.7/site-packages/networking_sfc-*egg*/entry_points.txt" + crudini --set --verbose $NEUTRON_CONF sfc drivers oc + crudini --set --verbose $NEUTRON_CONF flowclassifier drivers oc + if [[ `crudini --get neutron.conf DEFAULT service_plugins` != *"trunk"* ]]; then + crudini --set --verbose $NEUTRON_CONF DEFAULT service_plugins `crudini --get neutron.conf DEFAULT service_plugins`,trunk + fi + crudini --set --verbose $OVS_INI ovs phy_patch_ofport $PATCH_PORT + crudini --set --verbose $EGG_FILE networking_sfc.flowclassifier.drivers oc networking_sfc.services.flowclassifier.drivers.oc.driver:OCFlowClassifierDriver + crudini --set --verbose $EGG_FILE networking_sfc.sfc.agent_drivers oc networking_sfc.services.sfc.agent.extensions.oc.sfc_driver:SfcOCAgentDriver + crudini --set --verbose $EGG_FILE networking_sfc.sfc.drivers oc networking_sfc.services.sfc.drivers.oc.driver:OCSfcDriver + + systemctl restart neutron-server.service + systemctl restart neutron-openvswitch-agent.service +} + +function install_patch(){ + check_for_sfc + copy_sfc_patches + apply_patches + configure_sfc +} + +if [[ -z $PATCH_PATH ]]; then + Usage + exit 1 +fi +if [[ -z $PATCH_PORT ]]; then + Usage + echo -e "Please give the patch port of br-int that interfaces the switch " + echo -e "connected to the extreme switch. Choose the correct one from the following:\n" + ovs-vsctl list-ports br-int + echo -e "\nIt maybe something like int-br-vlan or int-extreme-br or the like" + exit 1 +fi + +PYTHON_MODULE_PATH="/usr/lib/python2.7/dist-packages" +PATCH_ARRAY=("sfc_agent-$VERSION.patch" "sfc_plugin-$VERSION.patch" "sfc_db-$VERSION.patch") +install_patch diff --git a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py index ab501d8f..c19e46fd 100644 --- a/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py +++ b/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py @@ -131,10 +131,11 @@ def delete_flow_rule(self, flowrule, flowrule_status): group_id = flowrule.get('next_group_id', None) if group_id and flowrule.get('group_refcnt', None) <= 1: self.br_int.delete_group(group_id=group_id) - for item in flowrule['next_hops']: - self.br_int.delete_flows( - table=ACROSS_SUBNET_TABLE, - dl_dst=item['mac_address']) + # Note(dpak): Check for repercussions. Loop previously depended on grp_rfcnt + for item in flowrule['next_hops']: + self.br_int.delete_flows( + table=ACROSS_SUBNET_TABLE, + dl_dst=item['mac_address']) if flowrule['ingress'] is not None: self._setup_destination_based_flows(flowrule, From 9fdd247344cd4fe3c71a3c429642c5c40f4e85af Mon Sep 17 00:00:00 2001 From: dpaks Date: Fri, 12 May 2017 17:52:36 +0530 Subject: [PATCH 15/16] script fixes --- apply_sfc_patch.sh | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/apply_sfc_patch.sh b/apply_sfc_patch.sh index a43f562e..436b1b59 100644 --- a/apply_sfc_patch.sh +++ b/apply_sfc_patch.sh @@ -11,19 +11,28 @@ Usage () { echo -e "applied ONLY on top of sfc rpm with version $VERSION.\n" } -# diff -Nur -x "*.py?" networking_sfc/db/ Projects/networking-sfc/networking_sfc/db/ > sfc_db-3.0.1.patch -# diff -Nur -x "*.py?" networking_sfc/services/sfc/agent/extensions/oc Projects/networking-sfc/networking_sfc/services/sfc/agent/extensions/oc > sfc_agent-3.0.1.patch -# +# diff -u networking_sfc.rpm/db/sfc_db.py networking_sfc/db/sfc_db.py > sfc_db-3.0.1.patch +# diff -u networking_sfc.rpm/db/flowclassifier_db.py networking_sfc/db/flowclassifier_db.py >> sfc_db-3.0.1.patch +# diff -Naur -x "*.py?" networking_sfc.rpm/services/sfc/agent/extensions/oc/ networking_sfc/services/sfc/agent/extensions/oc/ > sfc_agent-3.0.1.patch +# diff -Nau networking_sfc.rpm/services/sfc/drivers/oc/ networking_sfc/services/sfc/drivers/oc/ > sfc_plugin-3.0.1.patch +# diff -Nau networking_sfc.rpm/services/flowclassifier/drivers/oc/ networking_sfc/services/flowclassifier/drivers/oc/ >> sfc_plugin-3.0.1.patch function backup_files_to_be_modifed(){ echo "Backing up files that are to be modified while patching with $1." - if [[ $1 == "sfc_db.patch" ]]; then + if [[ $1 == "sfc_db-$VERSION.patch" ]]; then cp $PYTHON_MODULE_PATH/networking_sfc/db/sfc_db.py $PYTHON_MODULE_PATH/networking_sfc/db/sfc_db.py.bk cp $PYTHON_MODULE_PATH/networking_sfc/db/flowclassifier_db.py $PYTHON_MODULE_PATH/networking_sfc/db/flowclassifier_db.py.bk - elif [[ $1 == "sfc_agent.patch" ]]; then - cp $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py.bk - elif [[ $1 == "sfc_plugin.patch" ]]; then - cp $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/driver.py $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/driver.py.bk + elif [[ $1 == "sfc_agent-$VERSION.patch" ]]; then + mkdir -p $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc + cp $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/sfc_driver.py.bk 2>/dev/null + touch $PYTHON_MODULE_PATH/networking_sfc/services/sfc/agent/extensions/oc/__init__.py + elif [[ $1 == "sfc_plugin-$VERSION.patch" ]]; then + mkdir -p $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc + mkdir -p $PYTHON_MODULE_PATH/networking_sfc/services/flowclassifier/drivers/oc + cp $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/driver.py $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/driver.py.bk 2>/dev/null + cp $PYTHON_MODULE_PATH/networking_sfc/services/flowclassifier/drivers/oc/driver.py $PYTHON_MODULE_PATH/networking_sfc/services/flowclassifier/drivers/oc/driver.py.bk 2>/dev/null + touch $PYTHON_MODULE_PATH/networking_sfc/services/sfc/drivers/oc/__init__.py + touch $PYTHON_MODULE_PATH/networking_sfc/services/flowclassifier/drivers/oc/__init__.py else : fi @@ -64,9 +73,9 @@ function check_for_sfc(){ } function copy_sfc_patches(){ - cp $PATCH_PATH/sfc/sfc_plugin.patch $PYTHON_MODULE_PATH/. - cp $PATCH_PATH/sfc/sfc_agent.patch $PYTHON_MODULE_PATH/. - cp $PATCH_PATH/sfc/sfc_db.patch $PYTHON_MODULE_PATH/. + cp $PATCH_PATH/sfc-patches/sfc_plugin-$VERSION.patch $PYTHON_MODULE_PATH/. + cp $PATCH_PATH/sfc-patches/sfc_agent-$VERSION.patch $PYTHON_MODULE_PATH/. + cp $PATCH_PATH/sfc-patches/sfc_db-$VERSION.patch $PYTHON_MODULE_PATH/. } function configure_sfc(){ @@ -87,10 +96,17 @@ function configure_sfc(){ systemctl restart neutron-openvswitch-agent.service } +function cleanup(){ + rm -f $PYTHON_MODULE_PATH/sfc_agent-$VERSION.patch + rm -f $PYTHON_MODULE_PATH/sfc_plugin-$VERSION.patch + rm -f $PYTHON_MODULE_PATH/sfc_db-$VERSION.patch +} + function install_patch(){ check_for_sfc copy_sfc_patches apply_patches + cleanup configure_sfc } From 624650431385ec0bceacb1d17b6d0a0c52a45ab9 Mon Sep 17 00:00:00 2001 From: dpaks Date: Wed, 17 May 2017 14:09:21 +0530 Subject: [PATCH 16/16] scripts modifications --- apply_sfc_patch.sh | 10 ++++---- demo.sh | 59 +++++++++++++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/apply_sfc_patch.sh b/apply_sfc_patch.sh index 436b1b59..47b8ea72 100644 --- a/apply_sfc_patch.sh +++ b/apply_sfc_patch.sh @@ -6,7 +6,7 @@ PATCH_PATH=$1 PATCH_PORT=$2 Usage () { - echo -e "\n\nUsage: apply_sfc_patch.sh \n" + echo -e "\n\nUsage: apply_sfc_patch.sh \n" echo -e "\nThe patch SHOULD belong to version $VERSION and should be " echo -e "applied ONLY on top of sfc rpm with version $VERSION.\n" } @@ -84,12 +84,12 @@ function configure_sfc(){ EGG_FILE="/usr/lib/python2.7/site-packages/networking_sfc-*egg*/entry_points.txt" crudini --set --verbose $NEUTRON_CONF sfc drivers oc crudini --set --verbose $NEUTRON_CONF flowclassifier drivers oc - if [[ `crudini --get neutron.conf DEFAULT service_plugins` != *"trunk"* ]]; then - crudini --set --verbose $NEUTRON_CONF DEFAULT service_plugins `crudini --get neutron.conf DEFAULT service_plugins`,trunk + if [[ `crudini --get $NEUTRON_CONF DEFAULT service_plugins` != *"trunk"* ]]; then + crudini --set --verbose $NEUTRON_CONF DEFAULT service_plugins `crudini --get $NEUTRON_CONF DEFAULT service_plugins`,trunk fi crudini --set --verbose $OVS_INI ovs phy_patch_ofport $PATCH_PORT crudini --set --verbose $EGG_FILE networking_sfc.flowclassifier.drivers oc networking_sfc.services.flowclassifier.drivers.oc.driver:OCFlowClassifierDriver - crudini --set --verbose $EGG_FILE networking_sfc.sfc.agent_drivers oc networking_sfc.services.sfc.agent.extensions.oc.sfc_driver:SfcOCAgentDriver + crudini --set --verbose $EGG_FILE networking_sfc.sfc.agent_drivers ovs networking_sfc.services.sfc.agent.extensions.oc.sfc_driver:SfcOCAgentDriver crudini --set --verbose $EGG_FILE networking_sfc.sfc.drivers oc networking_sfc.services.sfc.drivers.oc.driver:OCSfcDriver systemctl restart neutron-server.service @@ -123,6 +123,6 @@ if [[ -z $PATCH_PORT ]]; then exit 1 fi -PYTHON_MODULE_PATH="/usr/lib/python2.7/dist-packages" +PYTHON_MODULE_PATH="/usr/lib/python2.7/site-packages" PATCH_ARRAY=("sfc_agent-$VERSION.patch" "sfc_plugin-$VERSION.patch" "sfc_db-$VERSION.patch") install_patch diff --git a/demo.sh b/demo.sh index d8b6f42d..7fd40431 100644 --- a/demo.sh +++ b/demo.sh @@ -2,9 +2,9 @@ ########################## FILL THIS INFO ##################################### # Note: Fill with hostnames. Blank, if compute does not exist. -computeA=compute-168 -computeB=compute-204 -computeC=compute-117 +computeA=node-204.localdomain +computeB=compute-5 +computeC=compute-210 # Note: Fill with SF and client glance image names. sf_image=sf @@ -18,7 +18,6 @@ if [[ -z $computeA && -z $computeB && -z $computeC ]]; then exit fi -source ~/devstack/openrc neutron service sf_id=`glance image-list | grep " $sf_image " | awk '{print $2}'` client_id=`glance image-list | grep " $client_image " | awk '{print $2}'` @@ -48,17 +47,22 @@ else fi if [ "$operation" == "create" ]; then + echo -e "\nCreating network Inspected_net" openstack network create inspected_net openstack subnet create --subnet-range 11.0.0.0/24 --network inspected_net inspected_subnet + sleep 50 + echo -e "\nCreating network Inspection_net" openstack network create inspection_net openstack subnet create --subnet-range 12.0.0.0/24 --network inspection_net inspection_subnet - openstack router create sfc-router + sleep 50 + #openstack router create sfc-router - openstack router add subnet sfc-router inspected_subnet - openstack router add subnet sfc-router inspection_subnet + #openstack router add subnet sfc-router inspected_subnet + #openstack router add subnet sfc-router inspection_subnet openstack port create --network inspected_net c1 openstack port create --network inspected_net c2 + openstack port create --network inspected_net c3 openstack port create --network inspection_net p1 openstack port create --network inspection_net p2 @@ -69,28 +73,34 @@ if [ "$operation" == "create" ]; then c1_id=`openstack port list -f value | grep " c1 " | awk '{print $1}'` c2_id=`openstack port list -f value | grep " c2 " | awk '{print $1}'` - + c3_id=`openstack port list -f value | grep " c3 " | awk '{print $1}'` + + echo -e "\nCreating Neutron Trunk port" openstack network trunk create --parent-port $p1_id trunk1 - echo "Launching VNF on Node $sf_compute..." + echo -e "\nLaunching VNF on Node $sf_compute..." nova boot --image $sf_image --flavor 3 --nic port-id=$p1_id\ --nic port-id=$p2_id --availability-zone nova:$sf_compute VNF sleep 5 - echo "Launching Client on Node $c1_compute..." - nova boot --image $client_image --flavor 2 --nic port-id=$c1_id --availability-zone nova:$c1_compute Client - echo "Launching Server on Node $c2_compute..." - nova boot --image $client_image --flavor 2 --nic port-id=$c2_id --availability-zone nova:$c2_compute Server + echo -e "\nLaunching Client1 on Node $c1_compute..." + nova boot --image $client_image --flavor 3 --nic port-id=$c1_id --availability-zone nova:$c1_compute Client1 + echo -e "\nLaunching Client2 on Node $sf_compute..." + nova boot --image $client_image --flavor 3 --nic port-id=$c3_id --availability-zone nova:$sf_compute Client2 + echo -e "\nLaunching Server on Node $c2_compute..." + nova boot --image $client_image --flavor 3 --nic port-id=$c2_id --availability-zone nova:$c2_compute Server sleep 5 vnf_id=`openstack server list | grep VNF | awk '{print $2}'` + echo -e "\nCreating a shadow port s1" openstack port create --network inspected_net --mac-address $p1_mac s1 s1_id=`openstack port list -f value | grep " s1 " | awk '{print $1}'` seg_id=`openstack network show inspected_net | grep segmentation_id | awk '{print $4}'` + echo -e "\nCreating a shadow port s2" openstack port create --network inspected_net --device $p2_id --mac-address $p2_mac s2 openstack network trunk set --subport port=$s1_id,segmentation-type=vlan,segmentation-id=$seg_id trunk1 @@ -99,15 +109,20 @@ if [ "$operation" == "create" ]; then sleep 3 + echo -e "\nCreating SFC Port Pair pp1." openstack port pair create --ingress s1 --egress s2 pp1 + echo -e "\nCreating SFC Port Pair Group ppg1." openstack port pair group create --port-pair pp1 ppg1 + echo -e "\nCreating SFC Flow Classifier fc1." openstack flow classifier create --protocol TCP --logical-source-port c1 --logical-destination-port c2 fc1 + echo -e "\nCreating SFC Flow Classifier fc2." + openstack flow classifier create --protocol TCP --logical-source-port c3 --logical-destination-port c2 fc2 while true; do read -p "Create or delete the port chain? (create/delete/exit) " op case $op in - create ) openstack port chain create --port-pair-group ppg1 --flow-classifier fc1 pc1;; - delete ) openstack port chain delete pc1;; + create ) echo -e "\nCreating SFC Port Chain pc1" && openstack port chain create --port-pair-group ppg1 --flow-classifier fc1 --flow-classifier fc2 pc1;; + delete ) echo -e "\nDeleting SFC Port Chain pc1" && openstack port chain delete pc1;; exit ) exit;; * ) echo "Please choose either of (create/delete/exit).";; esac @@ -116,20 +131,22 @@ if [ "$operation" == "create" ]; then else openstack port chain delete pc1 openstack flow classifier delete fc1 + openstack flow classifier delete fc2 openstack port pair group delete ppg1 openstack port pair delete pp1 s1_id=`openstack port list -f value | grep " s1 " | awk '{print $1}'` openstack network trunk unset --subport $s1_id trunk1 - openstack server delete Client Server VNF - + openstack server delete Client1 Server VNF Client2 + openstack network trunk delete trunk1 - openstack port delete s1 s2 c1 c2 p1 p2 + openstack port delete c1 c2 p1 p2 c3 s1 s2 - openstack router remove subnet sfc-router inspected_subnet - openstack router remove subnet sfc-router inspection_subnet - openstack router delete sfc-router + #openstack router remove subnet sfc-router inspected_subnet + #openstack router remove subnet sfc-router inspection_subnet + #openstack router delete sfc-router openstack network delete inspected_net inspection_net fi +