def remove_security_group_rule(self, secgroup, secgroup_rule): LOG.info(_LI("remove a rule %(rule)s to security group %(secgroup)s") % {'rule': secgroup_rule, 'secgroup': secgroup.name}) if self.get_datapath() is None: LOG.error(_LE("datapath is none")) return conj_id, priority = \ self._get_secgroup_conj_id_and_priority(secgroup.name) if conj_id is None: # this security group wasn't associated with a local port LOG.info(_LI("this security group %s wasn't associated with" " a local port"), secgroup.name) return # update the record of rules each of which specifies a same security # group as its parameter of remote group. remote_group_id = secgroup_rule.remote_group_id if remote_group_id is not None: associate_rules = self.remote_secgroup_ref.get(remote_group_id) if associate_rules is not None: del associate_rules[secgroup_rule.id] if len(associate_rules) == 0: del self.remote_secgroup_ref[remote_group_id] self._uninstall_security_group_rule_flows(secgroup_rule)
def test_infrastructure(self): try: topology = app_testing_objects.Topology(self.neutron, self.nb_api) subnet1 = topology.create_subnet(cidr='192.168.10.0/24') subnet2 = topology.create_subnet(cidr='192.168.11.0/24') port1 = subnet1.create_port() port2 = subnet2.create_port() topology.create_router([subnet1.subnet_id, subnet2.subnet_id]) LOG.info(_LI('Port1 name: {}').format(port1.tap.tap.name)) LOG.info(_LI('Port2 name: {}').format(port2.tap.tap.name)) test_utils.print_command(['ip', 'addr']) test_utils.print_command(['ovs-vsctl', 'show'], True) test_utils.print_command( ['ovs-ofctl', 'show', 'br-int'], True ) test_utils.print_command( ['ovs-ofctl', 'dump-flows', 'br-int'], True ) test_utils.print_command( ['ovsdb-client', 'dump', 'Open_vSwitch'], True ) except Exception as e: traceback = sys.exc_info()[2] try: topology.close() except Exception: pass # Ignore # Just calling raise may raise an exception from topology.close() raise e, None, traceback topology.close()
def _handle_dhcp_request(self, msg, pkt, lport): packet = ryu_packet.Packet(data=msg.data) in_port = msg.match.get("in_port") if isinstance(packet[3], str): dhcp_packet = dhcp.dhcp.parser(packet[3])[0] else: dhcp_packet = packet[3] dhcp_message_type = self._get_dhcp_message_type_opt(dhcp_packet) send_packet = None if dhcp_message_type == DHCP_DISCOVER: #DHCP DISCOVER send_packet = self._create_dhcp_offer( pkt, dhcp_packet, lport) LOG.info(_LI("sending DHCP offer for port IP %(port_ip)s" " port id %(port_id)s") % {'port_ip': lport.get_ip(), 'port_id': lport.get_id()}) elif dhcp_message_type == DHCP_REQUEST: #DHCP REQUEST send_packet = self._create_dhcp_ack( pkt, dhcp_packet, lport) LOG.info(_LI("sending DHCP ACK for port IP %(port_ip)s" " port id %(tunnel_id)s") % {'port_ip': lport.get_ip(), 'tunnel_id': lport.get_id()}) else: LOG.error(_LE("DHCP message type %d not handled"), dhcp_message_type) if send_packet: self._send_packet(self.get_datapath(), in_port, send_packet)
def _acquire_lock(oid): # generate temporary session id for this API context sid = _generate_session_id() # NOTE(nick-ma-z): we disallow subtransactions because the # retry logic will bust any parent transactions wait_lock_retries = LOCK_MAX_RETRIES retry_interval = LOCK_INIT_RETRY_INTERVAL while(wait_lock_retries > 0): try: session = db_api.get_session() with session.begin(): LOG.info(_LI("Try to get lock for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) row = _get_object_with_lock(session, oid, False) _update_lock(session, row, True, session_id=sid) LOG.info(_LI("Lock is acquired for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) return sid except orm_exc.NoResultFound: LOG.info(_LI("Lock has been obtained by other sessions. " "Wait here and retry.")) time.sleep(retry_interval) wait_lock_retries = wait_lock_retries - 1 # dynamically increase the retry_interval until it reaches # the maximum of interval and then return to the initial value. if retry_interval >= LOCK_MAX_RETRY_INTERVAL: retry_interval = LOCK_INIT_RETRY_INTERVAL else: retry_interval = retry_interval + LOCK_INC_RETRY_INTERVAL # NOTE(nick-ma-z): The lock cannot be acquired. raise df_exceptions.DBLockFailed(oid=oid, sid=sid)
def _port_status_handler(self, ev): msg = ev.msg reason = msg.reason port_no = msg.desc.port_no port_name = msg.desc.name ofproto = msg.datapath.ofproto if reason == ofproto.OFPPR_ADD: LOG.info(_LI("port added %s"), port_no) lport = self.db_store.get_local_port_by_name(port_name) if lport: lport.set_external_value('ofport', port_no) lport.set_external_value('is_local', True) self.notify_add_local_port(lport) elif reason == ofproto.OFPPR_DELETE: LOG.info(_LI("port deleted %s"), port_no) lport = self.db_store.get_local_port_by_name(port_name) if lport: self.notify_remove_local_port(lport) # Leave the last correct OF port number of this port elif reason == ofproto.OFPPR_MODIFY: LOG.info(_LI("port modified %s"), port_no) # TODO(oanson) Add notification else: LOG.info(_LI("Illeagal port state %(port_no)s %(reason)s") % {'port_no': port_no, 'reason': reason})
def _logical_port_process(self, lport, original_lport=None): chassis = lport.get_chassis() if chassis in (None, '', constants.DRAGONFLOW_VIRTUAL_PORT): LOG.debug(("Port %s has not been bound or it is a vPort ") % lport.get_id()) return chassis_to_ofport, lport_to_ofport = ( self.vswitch_api.get_local_ports_to_ofport_mapping()) network = self.get_network_id( lport.get_lswitch_id(), ) lport.set_external_value('local_network_id', network) if chassis == self.chassis_name: lport.set_external_value('is_local', True) ofport = lport_to_ofport.get(lport.get_id(), 0) if ofport != 0: lport.set_external_value('ofport', ofport) if original_lport is None: LOG.info(_LI("Adding new local logical port = %s") % str(lport)) self.open_flow_app.notify_add_local_port(lport) else: LOG.info(_LI("Updating local logical port = %(port)s, " "original port = %(original_port)s") % {'port': str(lport), 'original_port': str(original_lport)}) self.open_flow_app.notify_update_local_port(lport, original_lport) self.db_store.set_port(lport.get_id(), lport, True) else: LOG.info(_LI("Local logical port %s was not created yet") % str(lport)) else: lport.set_external_value('is_local', False) ofport = chassis_to_ofport.get(chassis, 0) if ofport != 0: lport.set_external_value('ofport', ofport) if original_lport is None: LOG.info(_LI("Adding new remote logical port = %s") % str(lport)) self.open_flow_app.notify_add_remote_port(lport) else: LOG.info(_LI("Updating remote logical port = %(port)s, " "original port = %(original_port)s") % {'port': str(lport), 'original_port': str(original_lport)}) self.open_flow_app.notify_update_remote_port( lport, original_lport) self.db_store.set_port(lport.get_id(), lport, False) else: # TODO(gampel) add handling for this use case # remote port but no tunnel to remote Host # if this should never happen raise an exception LOG.warning(_LW("No tunnel for remote logical port %s") % str(lport))
def ovs_sync_started(self): LOG.info(_LI("start aging")) canary_flow = self.get_canary_flow() if canary_flow is None: self.do_aging = False cookie.set_aging_cookie(const.GLOBAL_INIT_AGING_COOKIE) LOG.info(_LI("no canary table, don't do aging")) else: self._renew_aging_cookie(canary_flow.cookie) self.add_canary_flow(cookie.get_aging_cookie())
def _release_lock(oid, sid): # NOTE(nick-ma-z): we disallow subtransactions because the # retry logic will bust any parent transactions session = db_api.get_session() with session.begin(): LOG.info(_LI("Try to get lock for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) _lock_free_update(session, oid, lock_state=True, session_id=sid) LOG.info(_LI("Lock is released for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid})
def _acquire_lock(oid): # generate temporary session id for this API context sid = _generate_session_id() # NOTE(nick-ma-z): we disallow subtransactions because the # retry logic will bust any parent transactions session = db_api.get_session() with session.begin(): LOG.info(_LI("Try to get lock for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) _lock_free_update(session, oid, lock_state=False, session_id=sid) LOG.info(_LI("Lock is acquired for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) return sid
def logical_port_deleted(self, lport_id): lport = self.db_store.get_port(lport_id) if lport is None: return if lport.get_external_value('is_local'): LOG.info(_LI("Removing local Logical Port = %s") % lport.__str__()) self.open_flow_app.notify_remove_local_port(lport) self.db_store.delete_port(lport.get_id(), True) else: LOG.info(_LI("Removing remote Logical Port = %s") % lport.__str__()) self.open_flow_app.notify_remove_remote_port(lport) self.db_store.delete_port(lport.get_id(), False)
def chassis_created(self, chassis): # Check if tunnel already exists to this chassis t_ports = self.vswitch_api.get_tunnel_ports() remote_chassis_name = chassis.get_name() if self.chassis_name == remote_chassis_name: return for t_port in t_ports: if t_port.get_chassis_id() == remote_chassis_name: LOG.info(_LI("remote Chassis Tunnel already installed = %s") % chassis.__str__()) return # Create tunnel port to this chassis LOG.info(_LI("Adding tunnel to remote chassis = %s") % chassis.__str__()) self.vswitch_api.add_tunnel_port(chassis).execute()
def initialize(self): LOG.info(_LI("Starting DFMechDriver")) # When set to True, Nova plugs the VIF directly into the ovs bridge # instead of using the hybrid mode. self.vif_details = {portbindings.CAP_PORT_FILTER: True} self.vif_type = portbindings.VIF_TYPE_OVS self._set_base_port_binding() nb_driver_class = importutils.import_class(cfg.CONF.df.nb_db_class) self.nb_api = api_nb.NbApi( nb_driver_class(), use_pubsub=cfg.CONF.df.enable_df_pub_sub, is_neutron_server=True) self.nb_api.initialize(db_ip=cfg.CONF.df.remote_db_ip, db_port=cfg.CONF.df.remote_db_port) registry.subscribe(self.create_security_group, resources.SECURITY_GROUP, events.AFTER_CREATE) registry.subscribe(self.delete_security_group, resources.SECURITY_GROUP, events.BEFORE_DELETE) registry.subscribe(self.create_security_group_rule, resources.SECURITY_GROUP_RULE, events.AFTER_CREATE) registry.subscribe(self.delete_security_group_rule, resources.SECURITY_GROUP_RULE, events.BEFORE_DELETE)
def _delete_router_port(self, router_port): LOG.info(_LI("Removing logical router interface = %s") % router_port.__str__()) local_network_id = self.db_store.get_network_id( router_port.get_lswitch_id()) self.open_flow_app.notify_remove_router_port( router_port, local_network_id)
def update_subnet_postcommit(self, context): new_subnet = context.current old_subnet = context.original plugin_context = context._plugin_context try: dhcp_port = self._handle_update_subnet_dhcp(plugin_context, old_subnet, new_subnet) except Exception as e: LOG.exception(e) return None dhcp_address = self._get_ip_from_port(dhcp_port) self.nb_api.update_subnet( new_subnet['id'], new_subnet['network_id'], new_subnet['tenant_id'], name=new_subnet.get('name', df_const.DF_SUBNET_DEFAULT_NAME), nw_version=new_subnet['db_version'], enable_dhcp=new_subnet['enable_dhcp'], cidr=new_subnet['cidr'], dhcp_ip=dhcp_address, gateway_ip=new_subnet['gateway_ip'], dns_nameservers=new_subnet.get('dns_nameservers', []), host_routes=new_subnet.get('host_routes', [])) LOG.info(_LI("DFMechDriver: update subnet %s"), new_subnet['id']) return new_subnet
def create_port_postcommit(self, context): port = context.current ips = [ip['ip_address'] for ip in port.get('fixed_ips', [])] tunnel_key = self.nb_api.allocate_tunnel_key() # Router GW ports are not needed by dragonflow controller and # they currently cause error as they couldnt be mapped to # a valid ofport (or location) if port.get('device_owner') == n_const.DEVICE_OWNER_ROUTER_GW: chassis = None else: chassis = port.get('binding:host_id', None) self.nb_api.create_lport( id=port['id'], lswitch_id=port['network_id'], topic=port['tenant_id'], macs=[port['mac_address']], ips=ips, name=port.get('name', df_const.DF_PORT_DEFAULT_NAME), enabled=port.get('admin_state_up', None), chassis=chassis, tunnel_key=tunnel_key, version=port['db_version'], device_owner=port.get('device_owner', None), device_id=port.get('device_id', None), security_groups=port.get('security_groups', None), port_security_enabled=port.get(psec.PORTSECURITY, False), allowed_address_pairs=port.get(addr_pair.ADDRESS_PAIRS, None)) LOG.info(_LI("DFMechDriver: create port %s"), port['id']) return port
def chassis_deleted(self, chassis_id): LOG.info(_LI("Deleting tunnel to remote chassis = %s") % chassis_id) tunnel_ports = self.vswitch_api.get_tunnel_ports() for port in tunnel_ports: if port.get_chassis_id() == chassis_id: self.vswitch_api.delete_port(port).execute() return
def add_local_port(self, lport): network_id = lport.get_external_value('local_network_id') if self.get_datapath() is None: return lport_id = lport.get_id() tunnel_key = lport.get_tunnel_key() self.local_tunnel_to_pid_map[tunnel_key] = lport_id if not self._is_dhcp_enabled_on_network(lport, network_id): return if not self._is_port_a_vm(lport): return LOG.info(_LI("Regiter VM as DHCP client::port <%s>") % lport.get_id()) ofport = lport.get_external_value('ofport') parser = self.get_datapath().ofproto_parser ofproto = self.get_datapath().ofproto match = parser.OFPMatch() match.set_in_port(ofport) actions = [] actions.append(parser.OFPActionSetField(metadata=tunnel_key)) actions.append(parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)) inst = [self.get_datapath().ofproto_parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions)] self.mod_flow( self.get_datapath(), inst=inst, table_id=const.DHCP_TABLE, priority=const.PRIORITY_MEDIUM, match=match)
def _add_new_router_port(self, router, router_port): LOG.info(_LI("Adding new logical router interface = %s") % router_port.__str__()) local_network_id = self.db_store.get_network_id( router_port.get_lswitch_id()) self.open_flow_app.notify_add_router_port( router, router_port, local_network_id)
def _install_flows_check_valid_ip_and_mac(self, datapath, ofport, ip, mac): if netaddr.IPNetwork(ip).version == 6: LOG.info(_LI("IPv6 addresses are not supported yet")) return parser = datapath.ofproto_parser # Valid ip mac pair pass match = parser.OFPMatch(in_port=ofport, eth_src=mac, eth_type=ether.ETH_TYPE_IP, ipv4_src=ip) self.add_flow_go_to_table(datapath, const.EGRESS_PORT_SECURITY_TABLE, const.PRIORITY_HIGH, const.EGRESS_CONNTRACK_TABLE, match=match) # Valid arp request/reply pass match = parser.OFPMatch(in_port=ofport, eth_src=mac, eth_type=ether.ETH_TYPE_ARP, arp_spa=ip, arp_sha=mac) self.add_flow_go_to_table(datapath, const.EGRESS_PORT_SECURITY_TABLE, const.PRIORITY_HIGH, const.SERVICES_CLASSIFICATION_TABLE, match=match)
def _uninstall_flows_check_valid_ip_and_mac(self, datapath, ofport, ip, mac): if netaddr.IPNetwork(ip).version == 6: LOG.info(_LI("IPv6 addresses are not supported yet")) return parser = datapath.ofproto_parser # Remove valid ip mac pair pass match = parser.OFPMatch(in_port=ofport, eth_src=mac, eth_type=ether.ETH_TYPE_IP, ipv4_src=ip) self._remove_one_port_security_flow(datapath, const.PRIORITY_HIGH, match) # Remove valid arp request/reply pass match = parser.OFPMatch(in_port=ofport, eth_src=mac, eth_type=ether.ETH_TYPE_ARP, arp_spa=ip, arp_sha=mac) self._remove_one_port_security_flow(datapath, const.PRIORITY_HIGH, match)
def register_notification_callback(self, controller): self.controller = controller LOG.info(_LI("DB configuration sync finished, waiting for changes")) if not self.use_pubsub: self.driver.register_notification_callback( self.db_change_callback) self._read_db_changes_from_queue()
def _lock_free_update(session, id, lock_state=False, session_id=0): """Implement lock-free atomic update for the distributed lock :param session: the db session :type session: DB Session object :param id: the lock uuid :type id: string :param lock_state: the lock state to update :type lock_state: boolean :param session_id: the API session ID to update :type session_id: string :raises: RetryRequest() when the lock failed to update """ if not lock_state: # acquire lock search_params = {'object_uuid': id, 'lock': lock_state} update_params = {'lock': not lock_state, 'session_id': session_id, 'created_at': func.now()} else: # release or reset lock search_params = {'object_uuid': id, 'lock': lock_state, 'session_id': session_id} update_params = {'lock': not lock_state, 'session_id': 0} rows_update = session.query(models.DFLockedObjects).\ filter_by(**search_params).\ update(update_params, synchronize_session='fetch') if not rows_update: LOG.info(_LI('The lock for object %(id)s in session ' '%(sid)s cannot be updated.'), {'id': id, 'sid': session_id}) raise db_exc.RetryRequest(df_exc.DBLockFailed(oid=id, sid=session_id))
def _vm_port_deleted(self, ovs_port): ovs_port_id = ovs_port.get_id() lport_id = ovs_port.get_iface_id() lport = self.db_store.get_port(lport_id) if lport is None: lport = self.ovs_to_lport_mapping.get(ovs_port_id) if lport is None: return topic = lport.get('topic') del self.ovs_to_lport_mapping[ovs_port_id] self._del_from_topic_subscribed(topic, lport_id) return topic = lport.get_topic() LOG.info(_LI("The logical port(%s) is offline") % str(lport)) try: self.controller.logical_port_deleted(lport_id) except Exception: LOG.exception(_LE( 'Failed to process logical port offline event %s') % lport_id) finally: # TODO(duankebo) publish vm port offline later # currently we will not publish vm port offline event. # lport = self.nb_api.get_logical_port(lport_id) # if lport.get_chassis() == self.chassis_name: # self.nb_api.update_lport(lport.get_id(), chassis=None, # status='DOWN') del self.ovs_to_lport_mapping[ovs_port_id] self._del_from_topic_subscribed(topic, lport_id)
def ovs_port_deleted(self, ovs_port_id): """ Changes in ovs port status will be monitored by ovsdb monitor thread and notified to topology. This method is the entrance port to process port offline event @param ovs_port_id: @return : None """ ovs_port = self.ovs_ports.get(ovs_port_id) if ovs_port is None: return port_type = ovs_port.get_type() if port_type not in self.ovs_port_type: LOG.error(_LE("Unknown port offline: %s") % str(ovs_port)) return handler_name = '_' + port_type + '_port_deleted' try: handler = getattr(self, handler_name, None) if handler is not None: handler(ovs_port) else: LOG(_LI("%s is None.") % handler_name) except Exception: LOG.exception(_LE("Exception occurred when handling " "ovs port update event")) finally: del self.ovs_ports[ovs_port_id]
def floatingip_deleted(self, floatingip_id): floatingip = self.db_store.get_floatingip(floatingip_id) if not floatingip: return self.open_flow_app.notify_delete_floatingip(floatingip) LOG.info(_LI("Floatingip is deleted. Floatingip = %s") % str(floatingip))
def test_icmp_ping_pong(self): # the rules of the initial security group associated with port3 # only let icmp echo requests from port1 pass. self.policy.start(self.topology) self.policy.wait(30) # switch the associated security group with port3 to a new security # group, and rules of this security group only let icmp echo requests # from port2 pass. self._switch_to_another_security_group() time.sleep(test_utils.DEFAULT_CMD_TIMEOUT) self.policy.start(self.topology) self.policy.wait(30) # switch the associated security group with port3 to the initial # security group self._switch_to_another_security_group() time.sleep(test_utils.DEFAULT_CMD_TIMEOUT) self.policy.start(self.topology) self.policy.wait(30) ovs = test_utils.OvsFlowsParser() LOG.info(_LI("flows are: %s"), ovs.get_ovs_flows()) if len(self.policy.exceptions) > 0: raise self.policy.exceptions[0]
def delete(self): self._disconnect_tap_device_to_vswitch('br-int', self.tap.name) LOG.info(_LI('Closing tap interface {} ({})').format( self.tap.name, self.tap.fileno(), )) self.tap.close()
def _release_lock(oid, sid): # NOTE(nick-ma-z): we disallow subtransactions because the # retry logic will bust any parent transactions try: session = db_api.get_session() with session.begin(): LOG.info(_LI("Try to get lock for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) row = _get_object_with_lock(session, oid, True, session_id=sid) _update_lock(session, row, False, session_id=0) LOG.info(_LI("Lock is released for object %(oid)s in " "session %(sid)s."), {'oid': oid, 'sid': sid}) except orm_exc.NoResultFound: LOG.exception(_LE("The lock for object %(oid)s is lost in the " "session %(sid)s and obtained by other " "sessions."), {'oid': oid, 'sid': sid})
def logical_port_updated(self, lport): if self.db_store.get_port(lport.get_id()) is not None: # TODO(gsagie) support updating port return if lport.get_chassis() is None or ( lport.get_chassis() == constants.DRAGONFLOW_VIRTUAL_PORT): return chassis_to_ofport, lport_to_ofport = ( self.vswitch_api.get_local_ports_to_ofport_mapping()) network = self.get_network_id( lport.get_lswitch_id(), lport.get_topic(), ) lport.set_external_value('local_network_id', network) if lport.get_chassis() == self.chassis_name: ofport = lport_to_ofport.get(lport.get_id(), 0) self.db_store.set_port(lport.get_id(), lport, True) if ofport != 0: lport.set_external_value('ofport', ofport) lport.set_external_value('is_local', True) LOG.info(_LI("Adding new local Logical Port = %s") % lport.__str__()) self.open_flow_app.notify_add_local_port(lport) self.db_store.set_port(lport.get_id(), lport, True) else: LOG.info(_LI("Logical Local Port %s was not created yet ") % lport.__str__()) else: ofport = chassis_to_ofport.get(lport.get_chassis(), 0) self.db_store.set_port(lport.get_id(), lport, False) if ofport != 0: lport.set_external_value('ofport', ofport) lport.set_external_value('is_local', False) LOG.info(_LI("Adding new remote Logical Port = %s") % lport.__str__()) self.open_flow_app.notify_add_remote_port(lport) self.db_store.set_port(lport.get_id(), lport, False) else: #TODO(gampel) add handling for this use case #remote port but no tunnel to remote Host #if this should never happen raise an exception LOG.warning(_LW("No tunnel for Logical Remote Port %s ") % lport.__str__())
def publisher_deleted(self, uuid): publisher = self.db_store.get_publisher(uuid) if publisher: LOG.info(_LI('Deleting publisher: %s'), str(publisher)) self.nb_api.subscriber.unregister_listen_address( publisher.get_uri() ) self.db_store.delete_publisher(uuid)
def _update_security_group_rule_flows_by_addresses(self, secgroup, secgroup_rule, added_cidr, removed_cidr): conj_id, priority = self._get_secgroup_conj_id_and_priority(secgroup) if conj_id is None: LOG.error(_LE("the conj_id of the security group (%s) is none"), secgroup) return parser = self.get_datapath().ofproto_parser ofproto = self.get_datapath().ofproto rule_id = self._get_security_rule_mapping(secgroup_rule.get_id()) match_list = \ self._get_rule_flows_match_except_net_addresses(secgroup_rule) if secgroup_rule.get_ethertype() == n_const.IPv4: if secgroup_rule.get_direction() == 'ingress': table_id = const.INGRESS_SECURITY_GROUP_TABLE ipv4_match_item = "ipv4_src" else: table_id = const.EGRESS_SECURITY_GROUP_TABLE ipv4_match_item = "ipv4_dst" elif secgroup_rule.get_ethertype() == n_const.IPv6: # not support yet LOG.info(_LI("IPv6 rules are not supported yet")) return else: LOG.error(_LE("wrong ethernet type")) return actions = [ parser.NXActionConjunction(clause=1, n_clauses=2, id_=conj_id) ] action_inst = self.get_datapath(). \ ofproto_parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions) inst = [action_inst] for added_cidr_item in added_cidr: for match_item in match_list: parameters_merge = match_item.copy() parameters_merge[ipv4_match_item] = \ SGApp._get_network_and_mask(added_cidr_item) match = parser.OFPMatch(**parameters_merge) self.mod_flow(self.get_datapath(), cookie=SGApp._get_rule_cookie(rule_id), cookie_mask=COOKIE_FULLMASK, inst=inst, table_id=table_id, priority=priority, match=match) for removed_cidr_item in removed_cidr: for match_item in match_list: parameters_merge = match_item.copy() parameters_merge[ipv4_match_item] = \ SGApp._get_network_and_mask(removed_cidr_item) match = parser.OFPMatch(**parameters_merge) self.mod_flow(datapath=self.get_datapath(), table_id=table_id, priority=priority, match=match, command=ofproto.OFPFC_DELETE_STRICT, out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY)
def _install_security_group_rule_flows(self, secgroup, secgroup_rule): conj_id, priority = self._get_secgroup_conj_id_and_priority(secgroup) if conj_id is None: LOG.error(_LE("the conj_id of the security group %s is none"), secgroup) return parser = self.get_datapath().ofproto_parser ofproto = self.get_datapath().ofproto rule_id = self._get_security_rule_mapping(secgroup_rule.get_id()) remote_group_id = secgroup_rule.get_remote_group_id() remote_ip_prefix = secgroup_rule.get_remote_ip_prefix() ethertype = secgroup_rule.get_ethertype() if secgroup_rule.get_direction() == 'ingress': table_id = const.INGRESS_SECURITY_GROUP_TABLE ipv4_match_item = "ipv4_src" else: table_id = const.EGRESS_SECURITY_GROUP_TABLE ipv4_match_item = "ipv4_dst" match_list = \ self._get_rule_flows_match_except_net_addresses(secgroup_rule) actions = [ parser.NXActionConjunction(clause=1, n_clauses=2, id_=conj_id) ] action_inst = self.get_datapath(). \ ofproto_parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions) inst = [action_inst] if ethertype == n_const.IPv4: addresses_list = [{}] if remote_group_id is not None: aggregate_addresses_range = \ self.secgroup_aggregate_addresses.get(remote_group_id) addresses_list = [] if aggregate_addresses_range is not None: cidr_list = aggregate_addresses_range.iter_cidrs() for aggregate_address in cidr_list: addresses_list.append({ ipv4_match_item: SGApp._get_network_and_mask(aggregate_address) }) elif remote_ip_prefix is not None: addresses_list = [{ ipv4_match_item: SGApp._get_network_and_mask(remote_ip_prefix) }] for address_item in addresses_list: for match_item in match_list: parameters_merge = match_item.copy() parameters_merge.update(address_item) match = parser.OFPMatch(**parameters_merge) self.mod_flow(self.get_datapath(), cookie=SGApp._get_rule_cookie(rule_id), cookie_mask=COOKIE_FULLMASK, inst=inst, table_id=table_id, priority=priority, match=match) elif ethertype == n_const.IPv6: # not support yet LOG.info(_LI("IPv6 rules are not supported yet")) else: LOG.error(_LE("wrong ethernet type"))
def register_topic(self, topic): LOG.info(_LI('Register topic %s'), topic) if topic not in self.topic_list: self.topic_list.append(topic) return True return False
def unregister_topic(self, topic): LOG.info(_LI('Unregister topic %s'), topic) self.topic_list.remove(topic)
def test_default_flows(self): found_ingress_skip_flow = False found_egress_skip_flow = False found_ingress_default_drop_flow = False found_egress_default_drop_flow = False found_ingress_conntrack_established_pass_flow = False found_egress_conntrack_established_pass_flow = False found_ingress_conntrack_relative_not_new_pass_flow = False found_egress_conntrack_relative_not_new_pass_flow = False found_ingress_conntrack_relative_new_pass_flow = False found_egress_conntrack_relative_new_pass_flow = False found_ingress_conntrack_invalied_drop_flow = False found_egress_conntrack_invalied_drop_flow = False ovs = utils.OvsFlowsParser() flows = ovs.dump(self.integration_bridge) for flow in flows: if self._is_skip_flow(flow=flow, direction='ingress'): found_ingress_skip_flow = True elif self._is_skip_flow(flow=flow, direction='egress'): found_egress_skip_flow = True elif self._is_default_drop_flow(flow=flow, direction='ingress'): found_ingress_default_drop_flow = True elif self._is_default_drop_flow(flow=flow, direction='egress'): found_egress_default_drop_flow = True elif self._is_conntrack_established_pass_flow(flow=flow, direction='ingress'): found_ingress_conntrack_established_pass_flow = True elif self._is_conntrack_established_pass_flow(flow=flow, direction='egress'): found_egress_conntrack_established_pass_flow = True elif self._is_conntrack_relative_not_new_pass_flow( flow=flow, direction='ingress'): found_ingress_conntrack_relative_not_new_pass_flow = True elif self._is_conntrack_relative_not_new_pass_flow( flow=flow, direction='egress'): found_egress_conntrack_relative_not_new_pass_flow = True elif self._is_conntrack_relative_new_pass_flow( flow=flow, direction='ingress'): found_ingress_conntrack_relative_new_pass_flow = True elif self._is_conntrack_relative_new_pass_flow(flow=flow, direction='egress'): found_egress_conntrack_relative_new_pass_flow = True elif self._is_conntrack_invalid_drop_flow(flow=flow, direction='ingress'): found_ingress_conntrack_invalied_drop_flow = True elif self._is_conntrack_invalid_drop_flow(flow=flow, direction='egress'): found_egress_conntrack_invalied_drop_flow = True LOG.info(_LI("default flows are: %s"), ovs.get_ovs_flows(self.integration_bridge)) self.assertTrue(found_ingress_skip_flow) self.assertTrue(found_egress_skip_flow) self.assertTrue(found_ingress_default_drop_flow) self.assertTrue(found_egress_default_drop_flow) self.assertTrue(found_ingress_conntrack_established_pass_flow) self.assertTrue(found_egress_conntrack_established_pass_flow) self.assertTrue(found_ingress_conntrack_relative_not_new_pass_flow) self.assertTrue(found_egress_conntrack_relative_not_new_pass_flow) self.assertTrue(found_ingress_conntrack_relative_new_pass_flow) self.assertTrue(found_egress_conntrack_relative_new_pass_flow) self.assertTrue(found_ingress_conntrack_invalied_drop_flow) self.assertTrue(found_egress_conntrack_invalied_drop_flow)
def register_notification_callback(self, controller): self.controller = controller LOG.info(_LI("DB configuration sync finished, waiting for changes")) if not self.use_pubsub: self.driver.register_notification_callback(self.db_change_callback) self._read_db_changes_from_queue()
def apply_db_change(self, table, key, action, value): # determine if the action is allowed or not if action not in DB_ACTION_LIST: LOG.warning( _LW('Unknown action %(action)s for table ' '%(table)s'), { 'action': action, 'table': table }) return if action == 'sync': self.controller.run_sync() return elif action == 'dbrestart': self.db_recover_callback() return if 'secgroup' == table: if action == 'set' or action == 'create': secgroup = SecurityGroup(value) self.controller.security_group_updated(secgroup) elif action == 'delete': secgroup_id = key self.controller.security_group_deleted(secgroup_id) elif 'lport' == table: if action == 'create': lport = LogicalPort(value) self.controller.logical_port_created(lport) elif action == 'set': lport = LogicalPort(value) self.controller.logical_port_updated(lport) elif action == 'delete': lport_id = key self.controller.logical_port_deleted(lport_id) elif 'lrouter' == table: if action == 'set' or action == 'create': lrouter = LogicalRouter(value) self.controller.router_updated(lrouter) elif action == 'delete': lrouter_id = key self.controller.router_deleted(lrouter_id) elif 'chassis' == table: if action == 'set' or action == 'create': chassis = Chassis(value) self.controller.chassis_created(chassis) elif action == 'delete': chassis_id = key self.controller.chassis_deleted(chassis_id) elif 'lswitch' == table: if action == 'set' or action == 'create': lswitch = LogicalSwitch(value) self.controller.logical_switch_updated(lswitch) elif action == 'delete': lswitch_id = key self.controller.logical_switch_deleted(lswitch_id) elif 'floatingip' == table: if action == 'set' or action == 'create': floatingip = Floatingip(value) self.controller.floatingip_updated(floatingip) elif action == 'delete': floatingip_id = key self.controller.floatingip_deleted(floatingip_id) elif pub_sub_api.PUBLISHER_TABLE == table: if action == 'set' or action == 'create': publisher = Publisher(value) self.controller.publisher_updated(publisher) elif action == 'delete': self.controller.publisher_deleted(key) elif 'ovsinterface' == table: if action == 'set' or action == 'create': ovs_port = OvsPort(value) self.controller.ovs_port_updated(ovs_port) elif action == 'sync_finished': self.controller.ovs_sync_finished() elif action == 'sync_started': self.controller.ovs_sync_started() elif action == 'delete': ovs_port_id = key self.controller.ovs_port_deleted(ovs_port_id) elif 'log' == action: message = _LI('Log event (Info): ' 'table: %(table)s ' 'key: %(key)s ' 'action: %(action)s ' 'value: %(value)s') LOG.info( message, { 'table': str(table), 'key': str(key), 'action': str(action), 'value': str(value), }) else: LOG.warning(_LW('Unknown table %s'), table)
def test_associating_flows(self): network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api)) network_id = network.create(network={'name': 'test_network1'}) self.assertTrue(network.exists()) subnet_info = { 'network_id': network_id, 'cidr': '192.168.123.0/24', 'gateway_ip': '192.168.123.1', 'ip_version': 4, 'name': 'test_subnet1', 'enable_dhcp': True } subnet = self.store( objects.SubnetTestObj(self.neutron, self.nb_api, network_id=network_id)) subnet.create(subnet_info) self.assertTrue(subnet.exists()) security_group = self.store( objects.SecGroupTestObj(self.neutron, self.nb_api)) security_group_id = security_group.create() self.assertTrue(security_group.exists()) vm = self.store(objects.VMTestObj(self, self.neutron)) vm.create(network=network, security_groups=[security_group_id]) addresses = vm.server.addresses['test_network1'] self.assertIsNotNone(addresses) ip = addresses[0]['addr'] self.assertIsNotNone(ip) mac = addresses[0]['OS-EXT-IPS-MAC:mac_addr'] self.assertIsNotNone(mac) port = utils.wait_until_is_and_return( lambda: self._get_vm_port(ip, mac), exception=Exception('No port assigned to VM')) tunnel_key = port.get_tunnel_key() tunnel_key_hex = hex(tunnel_key) of_port = self._get_of_port(port.get_id()) self.assertIsNotNone(of_port) ovs = utils.OvsFlowsParser() flows_after_change = ovs.dump(self.integration_bridge) # Check if the associating flows were installed. ingress_associating_flow, egress_associating_flow = \ self._find_associating_flows(flows_after_change, of_port, tunnel_key_hex) LOG.info( _LI("flows after associating a port and a security group" " are: %s"), ovs.get_ovs_flows(self.integration_bridge)) self.assertIsNotNone(ingress_associating_flow) self.assertIsNotNone(egress_associating_flow) vm.close() time.sleep(utils.DEFAULT_CMD_TIMEOUT) flows_after_update = ovs.dump(self.integration_bridge) # Check if the associating flows were removed. ingress_associating_flow, egress_associating_flow = \ self._find_associating_flows(flows_after_update, of_port, tunnel_key_hex) self.assertIsNone(ingress_associating_flow) self.assertIsNone(egress_associating_flow)
def test_rule_flows(self): network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api)) network_id = network.create(network={'name': 'test_network2'}) self.assertTrue(network.exists()) subnet_info = { 'network_id': network_id, 'cidr': '192.168.124.0/24', 'gateway_ip': '192.168.124.1', 'ip_version': 4, 'name': 'test_subnet4', 'enable_dhcp': True } subnet = self.store( objects.SubnetTestObj(self.neutron, self.nb_api, network_id=network_id)) subnet.create(subnet_info) security_group = self.store( objects.SecGroupTestObj(self.neutron, self.nb_api)) security_group_id = security_group.create() self.assertTrue(security_group.exists()) ingress_rule_info = { 'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': '8000', 'port_range_max': '8100', 'remote_ip_prefix': '192.168.124.0/24' } ingress_rule_id = security_group.rule_create(secrule=ingress_rule_info) self.assertTrue(security_group.rule_exists(ingress_rule_id)) egress_rule_info = { 'ethertype': 'IPv4', 'direction': 'egress', 'protocol': '17', 'port_range_min': '53', 'port_range_max': '53', 'remote_group_id': security_group_id } egress_rule_id = security_group.rule_create(secrule=egress_rule_info) self.assertTrue(security_group.rule_exists(egress_rule_id)) vm = self.store(objects.VMTestObj(self, self.neutron)) vm.create(network=network, security_groups=[security_group_id]) time.sleep(utils.DEFAULT_CMD_TIMEOUT) ovs = utils.OvsFlowsParser() flows_after_change = ovs.dump(self.integration_bridge) LOG.info(_LI("flows after adding rules are: %s"), ovs.get_ovs_flows(self.integration_bridge)) # Check if the rule flows were installed. self._check_rule_flows(flows_after_change, True) vm.close()
def __call__(self, policy, rule, port_thread, buf): pkt = ryu.lib.packet.packet.Packet(buf) LOG.info(_LI('LogAction: Got packet: {}').format(str(pkt)))
def _disassociate_floatingip(self, floatingip): self.db_store.delete_floatingip(floatingip.get_id()) self.open_flow_app.notify_disassociate_floatingip(floatingip) LOG.info( _LI("Floatingip is disassociated from port." " Floatingip = %s") % str(floatingip))
def _add_new_router_port(self, router, router_port): LOG.info(_LI("Adding new logical router interface = %s"), router_port) local_network_id = self.db_store.get_network_id( router_port.get_lswitch_id() ) datapath = self.get_datapath() if datapath is None: return parser = datapath.ofproto_parser ofproto = datapath.ofproto mac = router_port.get_mac() tunnel_key = router_port.get_tunnel_key() dst_ip = router_port.get_ip() # Add router ARP & ICMP responder for IPv4 Addresses is_ipv4 = netaddr.IPAddress(dst_ip).version == 4 if is_ipv4: arp_responder.ArpResponder( self, local_network_id, dst_ip, mac).add() icmp_responder.ICMPResponder(self, dst_ip, mac).add() # If router interface IP, send to output table if is_ipv4: match = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP, metadata=local_network_id, ipv4_dst=dst_ip) else: match = parser.OFPMatch(eth_type=ether.ETH_TYPE_IPV6, metadata=local_network_id, ipv6_dst=dst_ip) actions = [] actions.append(parser.OFPActionSetField(reg7=tunnel_key)) action_inst = self.get_datapath().ofproto_parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions) goto_inst = parser.OFPInstructionGotoTable(const.EGRESS_TABLE) inst = [action_inst, goto_inst] self.mod_flow( datapath, inst=inst, table_id=const.L3_LOOKUP_TABLE, priority=const.PRIORITY_HIGH, match=match) #add dst_mac=gw_mac l2 goto l3 flow match = parser.OFPMatch() match.set_metadata(local_network_id) match.set_dl_dst(haddr_to_bin(mac)) goto_inst = parser.OFPInstructionGotoTable(const.L3_LOOKUP_TABLE) inst = [goto_inst] self.mod_flow( self.get_datapath(), inst=inst, table_id=const.L2_LOOKUP_TABLE, priority=const.PRIORITY_HIGH, match=match) # Match all possible routeable traffic and send to controller for port in router.get_ports(): if port.get_id() != router_port.get_id(): # From this router interface to all other interfaces self._add_subnet_send_to_controller(local_network_id, port.get_cidr_network(), port.get_cidr_netmask(), port.get_tunnel_key()) # From all the other interfaces to this new interface router_port_net_id = self.db_store.get_network_id( port.get_lswitch_id(), ) self._add_subnet_send_to_controller( router_port_net_id, router_port.get_cidr_network(), router_port.get_cidr_netmask(), tunnel_key)
def publisher_updated(self, publisher): self.db_store.update_publisher(publisher.get_id(), publisher) LOG.info(_LI('Registering to new publisher: %s'), str(publisher)) self.nb_api.subscriber.register_listen_address(publisher.get_uri())
def db_change_callback(self, table, key, action, value, topic=None): update = DbUpdate(table, key, action, value, topic=topic) LOG.info(_LI("Pushing Update to Queue: %s"), update) self._queue.put(update) eventlet.sleep(0)
def _add_new_security_group_rule(self, secgroup, secgroup_rule): LOG.info(_LI("Adding new secgroup rule = %s") % secgroup_rule) self.open_flow_app.notify_add_security_group_rule( secgroup, secgroup_rule)
def _delete_security_group_rule(self, secgroup, secgroup_rule): LOG.info(_LI("Removing secgroup rule = %s") % secgroup_rule) self.open_flow_app.notify_remove_security_group_rule( secgroup, secgroup_rule)
def _install_network_flows_for_vlan(self, segmentation_id, physical_network, local_network_id): LOG.info(_LI("Install network flows on first vlan up")) # L2_LOOKUP for Remote ports datapath = self.get_datapath() parser = datapath.ofproto_parser ofproto = datapath.ofproto match = parser.OFPMatch() addint = haddr_to_bin('00:00:00:00:00:00') add_mask_int = haddr_to_bin('01:00:00:00:00:00') match.set_dl_dst_masked(addint, add_mask_int) match.set_metadata(local_network_id) inst = [parser.OFPInstructionGotoTable(const.EGRESS_TABLE)] self.mod_flow(datapath=datapath, inst=inst, table_id=const.L2_LOOKUP_TABLE, priority=const.PRIORITY_MEDIUM, match=match) # EGRESS for Remote ports # Table=Egress # Match: metadata=network_id # Actions: mod_vlan, output:patch match = parser.OFPMatch(metadata=local_network_id) actions = [ parser.OFPActionPushVlan(ether.ETH_TYPE_8021Q), parser.OFPActionSetField(vlan_vid=(segmentation_id & 0x1fff) | 0x1000) ] action_inst = parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions) goto_inst = parser.OFPInstructionGotoTable(const.EGRESS_EXTERNAL_TABLE) inst = [action_inst, goto_inst] self.mod_flow(datapath=datapath, inst=inst, table_id=const.EGRESS_TABLE, priority=const.PRIORITY_LOW, match=match) # Add EGRESS port according to physical_network self._install_output_to_physical_patch(physical_network, local_network_id) # Ingress # Match: dl_vlan=vlan_id, # Actions: metadata=network_id, # goto 'Destination Port Classification' match = parser.OFPMatch() match.set_vlan_vid(segmentation_id) actions = [ parser.OFPActionSetField(metadata=local_network_id), parser.OFPActionPopVlan() ] action_inst = parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions) goto_inst = parser.OFPInstructionGotoTable( const.INGRESS_DESTINATION_PORT_LOOKUP_TABLE) inst = [action_inst, goto_inst] self.mod_flow(datapath=datapath, inst=inst, table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE, priority=const.PRIORITY_LOW, match=match)
def _logical_port_process(self, lport, original_lport=None): chassis = lport.get_chassis() local_network_id = self.get_network_id(lport.get_lswitch_id(), ) lswitch = self.db_store.get_lswitch(lport.get_lswitch_id()) if lswitch is not None: network_type = lswitch.get_network_type() segment_id = lswitch.get_segment_id() physical_network = lswitch.get_physical_network() lport.set_external_value('network_type', network_type) if segment_id is not None: lport.set_external_value('segmentation_id', int(segment_id)) if physical_network: lport.set_external_value('physical_network', physical_network) lport.set_external_value('local_network_id', local_network_id) if chassis == self.chassis_name: lport.set_external_value('is_local', True) ofport = self.vswitch_api.get_port_ofport_by_id(lport.get_id()) if ofport: lport.set_external_value('ofport', ofport) self.db_store.set_port(lport.get_id(), lport, True) if original_lport is None: LOG.info( _LI("Adding new local logical port = %s") % str(lport)) self.open_flow_app.notify_add_local_port(lport) else: LOG.info( _LI("Updating local logical port = %(port)s, " "original port = %(original_port)s") % { 'port': str(lport), 'original_port': str(original_lport) }) self.open_flow_app.notify_update_local_port( lport, original_lport) else: LOG.info( _LI("Local logical port %s was not created yet") % str(lport)) else: lport.set_external_value('is_local', False) ofport = self.vswitch_api.get_chassis_ofport(chassis) if ofport: lport.set_external_value('ofport', ofport) self.db_store.set_port(lport.get_id(), lport, False) if original_lport is None: LOG.info( _LI("Adding new remote logical port = %s") % str(lport)) self.open_flow_app.notify_add_remote_port(lport) else: LOG.info( _LI("Updating remote logical port = %(port)s, " "original port = %(original_port)s") % { 'port': str(lport), 'original_port': str(original_lport) }) self.open_flow_app.notify_update_remote_port( lport, original_lport) else: # TODO(gampel) add handling for this use case # remote port but no tunnel to remote Host # if this should never happen raise an exception LOG.warning( _LW("No tunnel for remote logical port %s") % str(lport))
def register_topic(self, topic): LOG.info(_LI('Register topic %s'), topic) topic = topic.encode('ascii', 'ignore') if topic not in self.topic_list: self.topic_list.append(topic)
def _associate_floatingip(self, floatingip): self.db_store.update_floatingip(floatingip.get_id(), floatingip) self.open_flow_app.notify_associate_floatingip(floatingip) LOG.info( _LI("Floatingip is associated with port. Floatingip = %s") % str(floatingip))