def test_create_router_interface(self): router = self.store(objects.RouterTestObj(self.neutron, self.nb_api)) network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api)) network_id = network.create() self.assertTrue(network.exists()) subnet = self.store(objects.SubnetTestObj( self.neutron, self.nb_api, network_id, )) subnet_id = subnet.create() router_id = router.create() self.assertTrue(router.exists()) subnet_msg = {'subnet_id': subnet_id} port = self.neutron.add_interface_router(router_id, body=subnet_msg) port2 = self.nb_api.get(l2.LogicalPort(id=port['port_id'])) self.assertIsNotNone(port2) router.close() utils.wait_until_none( lambda: self.nb_api.get(l2.LogicalPort(id=port['port_id'])), exception=Exception('Port was not deleted') ) subnet.close() network.close() self.assertFalse(router.exists()) self.assertFalse(network.exists())
def db_change_callback(self, table, key, action, value, topic=None): """ Called from nb_api on db update. :param table: :param key: :param action: :param value: :param topic: """ print( "L2 App: Received Update for table {} and key {} action {}".format( table, key, action)) # These updates are only required if data is cached locally if self.USE_CACHE: if table == 'lport' and (action == 'create' or action == 'update'): # check if datapath of port can be found in cache cache_dpid = None for dpid, port_id in self.cache_ports_by_datapath_id.iteritems( ): if port_id == key: # this value needs to bee updated # updating values while iterating isn't a good practice: Exit loop and apply update cache_dpid = dpid break if not cache_dpid is None: # values was in cache -> update self.cache_ports_by_datapath_id[cache_dpid][ key] = self.nb_api.get(l2.LogicalPort(id=key)) else: # port not in cache lport = self.nb_api.get(l2.LogicalPort(id=key)) dpid = lport.lswitch.id self.cache_ports_by_datapath_id[dpid][lport.id] = lport
def test_port_version(self): network = objects.NetworkTestObj(self.neutron, self.nb_api) self.addCleanup(network.close) network_id = network.create() self.assertTrue(network.exists()) subnet = objects.SubnetTestObj(self.neutron, self.nb_api, network_id) self.addCleanup(subnet.close) subnet.create() self.assertTrue(subnet.exists()) port = objects.PortTestObj(self.neutron, self.nb_api, network_id) self.addCleanup(port.close) port_id = port.create() self.assertTrue(port.exists()) prev_version = self.nb_api.get(l2.LogicalPort(id=port_id)).version port.update() self.assertTrue(port.exists()) version = self.nb_api.get(l2.LogicalPort(id=port_id)).version self.assertGreater(version, prev_version) port.close() self.assertFalse(port.exists()) subnet.close() self.assertFalse(subnet.exists()) network.close() self.assertFalse(network.exists())
def db_change_callback(self, table, key, action, value, topic=None): """ Called from nb_api on db update. Routing: When logicalport is updated with a host ip (this happens while arp request/responses): for all routers install route to this ip :param table: :param key: :param action: :param value: :param topic: """ if self.USE_CACHE: # Update cache if action == 'create' or action == 'set': if table == 'lport': self.cache_logical_port_by_port_id[key] = self.nb_api.get(l2.LogicalPort(id=key)) if table == 'lrouter': self.cache_logical_router_by_dpid[key] = self.nb_api.get(l3.LogicalRouter(id=key)) if action == 'del': if table == 'lport': # default if key does not exists is None self.cache_logical_port_by_port_id.pop(key, None) if table == 'lrouter': self.cache_logical_router_by_dpid.pop(key, None) print("L3 App: Received Update for table {} and key {} action {}".format(table, key, action)) if action == 'set': if table == 'lport': if self.USE_CACHE: updated_port = self.cache_logical_port_by_port_id[key] else: updated_port = self.nb_api.get(l2.LogicalPort(id=key)) if len(updated_port.ips) is not 0: for ip in updated_port.ips: # new ip discovered # install route on every datapath # only update the other datapaths for dpid, datapath in self.cache_datapath_by_dpid.iteritems(): out_port, new_src_mac, new_dst_mac = self.get_next_hop(dpid, ip) if out_port is None: continue out_port_id = "{}:{}".format(dpid, out_port) lout_port = self.nb_api.get(l2.LogicalPort(id=out_port_id)) if ip in lout_port.ips: continue # else add new ip and install flow lout_port.ips.append(ip) self.nb_api.update(lout_port) # install flow print "L3 IP via pubsub: installing flow on {}: out_port: {} src_mac:" \ " {} dst_mac: {}, ip: {}".format(datapath.id, out_port, new_src_mac, new_dst_mac, ip) self.add_flow_gateway_for_ip(datapath, int(out_port), ip, new_src_mac, new_dst_mac)
def _get_lport(self, port_id, topic=None): if topic is None: lean_lport = l2.LogicalPort(id=port_id) else: lean_lport = l2.LogicalPort(id=port_id, topic=topic) lport = self.db_store.get_one(lean_lport) if lport is None: lport = self.nb_api.get(lean_lport) return lport
def update_port_ip(self, dpid, port, ip): """ Update Database with learned IPs from port :param dpid: :param port: :param ip: :return: """ # TODO Connection between mac and ip of host? if self.nb_api is None: self.nb_api = api_nb.NbApi.get_instance(False) port_id = "{}:{}".format(dpid, port) try: lport = self.nb_api.get(l2.LogicalPort(id=port_id)) for ip_addr_obj in lport.ips: if str(ip_addr_obj) == ip: # already learned return lport.ips.append(ip) self.nb_api.update(lport) # TODO: Remove old ips except DBKeyNotFound: # TODO: Create Port? print "Key not Found!!"
def packet_in_handler(self, event): msg = event.msg pkt = ryu_packet.Packet(msg.data) pkt_ip = pkt.get_protocol(ipv4.ipv4) if not pkt_ip: LOG.error("No support for non IPv4 protocol") return unique_key = msg.match.get('reg6') lport = self.db_store.get_one( l2.LogicalPort(unique_key=unique_key), index=l2.LogicalPort.get_index('unique_key'), ) port_rate_limiter = self._port_rate_limiters[lport.id] if port_rate_limiter(): self._block_port_dhcp_traffic( unique_key, self.block_hard_timeout) LOG.warning("pass rate limit for %(port_id)s blocking DHCP " "traffic for %(time)s sec", {'port_id': lport.id, 'time': self.block_hard_timeout}) return if not self.db_store.get_one(lport): LOG.error("Port %s no longer found.", lport.id) return try: self._handle_dhcp_request(pkt, lport) except Exception: LOG.exception("Unable to handle packet %s", msg)
def _vm_port_deleted(self, ovs_port): ovs_port_id = ovs_port.id lport_id = ovs_port.iface_id lport = self.db_store2.get_one(l2.LogicalPort(id=lport_id)) if lport is None: lport = self.ovs_to_lport_mapping.get(ovs_port_id) if lport is None: return topic = lport.topic del self.ovs_to_lport_mapping[ovs_port_id] self._del_from_topic_subscribed(topic, lport_id) return topic = lport.topic LOG.info("The logical port(%s) is offline", lport) try: self.controller.delete(lport) except Exception: LOG.exception('Failed to process logical port offline event %s', lport_id) finally: self.controller.notify_port_status(ovs_port, n_const.PORT_STATUS_DOWN) migration = self.nb_api.get_lport_migration(lport_id) if migration and migration.get('migration'): LOG.info("Sending migrating event for %s", lport_id) self.nb_api.notify_migration_event(lport_id, lport) del self.ovs_to_lport_mapping[ovs_port_id] self._del_from_topic_subscribed(topic, lport_id)
def _process_ovs_tunnel_port(self, ovs_port, action): tunnel_type = ovs_port.tunnel_type if not tunnel_type: return lswitches = self.db_store.get_all( l2.LogicalSwitch(network_type=tunnel_type), l2.LogicalSwitch.get_index('network_type')) for lswitch in lswitches: index = l2.LogicalPort.get_indexes()['lswitch_id'] lports = self.db_store.get_all(l2.LogicalPort(lswitch=lswitch), index=index) for lport in lports: if lport.is_local: continue # Update of virtual tunnel port should update remote port in # the lswitch of same type. try: if action == "set": self.controller.update(lport) else: self.controller.delete(lport) except Exception: LOG.exception( "Failed to process logical port" "when %(action)s tunnel %(lport)s", { 'action': action, 'lport': lport })
def update_mac_to_port(self, dpid, mac, port, use_cache=True): """ Can be inconsistent with db :param dpid: :param mac: :param port: """ port_id = self.create_port_id(dpid, port) dpid = str(dpid) # TODO: check for host migration if use_cache: # check cache: if dpid in self.cache_ports_by_datapath_id.keys(): if port_id in self.cache_ports_by_datapath_id[dpid]: cport = self.cache_ports_by_datapath_id[dpid][port_id] if mac not in cport.macs: # update cache cport.macs.append(mac) # update db self.nb_api.update(cport) else: # new learned port! # write to database self.cache_ports_by_datapath_id.setdefault( dpid, {}) # create empty entry if key does not exists self.cache_ports_by_datapath_id[dpid][ port_id] = self.create_port(dpid, mac, port) else: try: lport = self.nb_api.get(l2.LogicalPort(id=port_id)) if lport is not None and mac not in lport.macs: lport.macs.append(mac) self.nb_api.update(lport) except DBKeyNotFound: self.create_port(dpid, mac, port)
def _vm_port_deleted(self, ovs_port): ovs_port_id = ovs_port.id lport_id = ovs_port.iface_id lport = self.db_store.get_one(l2.LogicalPort(id=lport_id)) if lport is None: lport = self.ovs_to_lport_mapping.get(ovs_port_id) if lport is None: return topic = lport.topic del self.ovs_to_lport_mapping[ovs_port_id] self._del_from_topic_subscribed(topic, lport_id) return topic = lport.topic LOG.info("The logical port(%s) is offline", lport) try: self.controller.delete(lport) except Exception: LOG.exception('Failed to process logical port offline event %s', lport_id) finally: self.controller.notify_port_status(ovs_port, n_const.PORT_STATUS_DOWN) migration_obj = self.nb_api.get(migration.Migration(id=lport_id)) if migration_obj and migration_obj.chassis: LOG.info("Sending migrating event for %s", lport_id) migration_obj.lport = lport_id migration_obj.status = migration.MIGRATION_STATUS_SRC_UNPLUG self.nb_api.update(migration_obj) del self.ovs_to_lport_mapping[ovs_port_id] self._del_from_topic_subscribed(topic, lport_id)
def _setup_local_route(self, llroute): lport = self.nb_api.get(l2.LogicalPort(id=llroute.port)) lswitch = lport.lswitch.get_object() if not lswitch: lswitch = self.nb_api.get(lport.lswitch) network_id = lswitch.unique_key match = self.get_mpls_label_match(llroute.label) actions = [ self.parser.OFPActionPopMpls(ethertype=ethernet.ether.ETH_TYPE_IP), self.parser.OFPActionSetField(eth_dst=lport.mac), self.parser.OFPActionSetField(reg7=lport.unique_key), self.parser.OFPActionSetField(metadata=network_id) ] action_inst = self.parser.OFPInstructionActions( self.ofproto.OFPIT_APPLY_ACTIONS, actions) goto_inst = self.parser.OFPInstructionGotoTable( const.INGRESS_DISPATCH_TABLE) inst = [action_inst, goto_inst] self.mod_flow( table_id=const.INGRESS_MPLS_TABLE, priority=const.PRIORITY_HIGH, inst=inst, match=match, )
def _handle_egress_invalid_ttl(self, event): LOG.debug("Get an invalid TTL packet at table %s", const.EGRESS_DNAT_TABLE) if self.egress_ttl_invalid_handler_rate_limit(): LOG.warning("Get more than %(rate)s TTL invalid " "packets per second at table %(table)s", {'rate': self.conf.dnat_ttl_invalid_max_rate, 'table': const.EGRESS_DNAT_TABLE}) return msg = event.msg pkt = packet.Packet(msg.data) e_pkt = pkt.get_protocol(ethernet.ethernet) port_key = msg.match.get('reg6') lport = self.db_store.get_one( l2.LogicalPort(unique_key=port_key), index=l2.LogicalPort.get_index('unique_key'), ) floatingip = self.db_store.get_one( l3.FloatingIp(lport=lport.id), index=l3.FloatingIp.get_index('lport'), ) if floatingip is None: LOG.warning("The invalid TTL packet's destination mac %s " "can't be recognized.", e_pkt.src) return icmp_ttl_pkt = icmp_error_generator.generate( icmp.ICMP_TIME_EXCEEDED, icmp.ICMP_TTL_EXPIRED_CODE, msg.data, floatingip.floating_ip_address, pkt) self.dispatch_packet(icmp_ttl_pkt, port_key)
def _install_flow_by_packet_and_continue(self, pkt_ip, network_id, msg): """ Install the routing flows by the information in the packet, and have the packet continue along the pipelone. :param pkt_ip: IP header on the packet (IPv4 or IPv6) :type pkt_ip: ryu.packet.ipv4 or ryu.packet.ipv6 :param network_id: The source network from which the packet arrived :type network_id: Integer :param msg: Packet in message :type msg: ryu.ofproto.ofproto_v<version>_parser.OFPPacketIn """ ip_addr = netaddr.IPAddress(pkt_ip.dst) router_unique_key = msg.match.get('reg5') router = self.db_store.get_all( l3.LogicalRouter(unique_key=router_unique_key), l3.LogicalRouter.get_index('unique_key')) for router_port in router.ports: if ip_addr in router_port.network: index = l2.LogicalPort.get_index('lswitch_id') dst_ports = self.db_store.get_all(l2.LogicalPort( lswitch=l2.LogicalSwitch(id=router_port.lswitch.id)), index=index) for out_port in dst_ports: if out_port.ip == ip_addr: self._install_flow_by_ports_and_continue( router_port, out_port, msg, network_id) return
def run_server(nb_api): topic = str(uuid.uuid4()) publisher = str(uuid.uuid4()) lswitch_name = 'lswitch0' nb_api.create_publisher(publisher, topic, last_activity_timestamp=time.time()) nb_api.create(l2.LogicalSwitch(id=lswitch_name, topic=topic)) start = time.time() for idx in range(cfg.CONF.df_db_test.count): nb_api.create( l2.LogicalPort(id='lport{}'.format(idx), lswitch=lswitch_name, topic=topic, timestamp=time.time())) nb_api.delete_publisher(publisher, topic) end = time.time() data_str = [ str(end - start), str(cfg.CONF.df_db_test.count), str(cfg.CONF.df_db_test.count / (end - start)) ] outfile_name = '{}/test_db_server.{}'.format( cfg.CONF.df_db_test.output_folder, uuid.uuid4()) outfile = open(outfile_name, 'w') outfile.write('total, count, r/sec\n') outfile.write(', '.join(data_str)) outfile.write('\n') outfile.close() sys.exit(0)
def update_port_postcommit(self, context): updated_port = context.current topic = df_utils.get_obj_topic(updated_port) lean_port = l2.LogicalPort(id=updated_port['id'], topic=topic) if not self.nb_api.get(lean_port): # REVISIT(xiaohhui): Should we unify the check before update nb db? LOG.debug( "The port %s has been deleted from dragonflow NB DB, " "by concurrent operation.", updated_port['id']) return # Here we do not want port status update to trigger # sending event to other compute node. if (cfg.CONF.df.enable_neutron_notifier and n_const.DEVICE_OWNER_COMPUTE_PREFIX in updated_port['device_owner'] and context.status != context.original_status and (context.status == n_const.PORT_STATUS_DOWN or context.status == n_const.PORT_STATUS_ACTIVE)): return None lport = neutron_l2.logical_port_from_neutron_port(updated_port) # Update topic for FIP ports if lport.topic == '': lport.topic = self._get_lswitch_topic(updated_port) self.nb_api.update(lport) LOG.info("DFMechDriver: update port %s", updated_port['id']) return updated_port
def test_floatingip_updated(self, mock_get_one, mock_get_fip, mock_assoc, mock_is_valid, mock_update): lport_id = 'fake_lport_id' fip_id = 'fake_fip_id' fip = self._get_mock_floatingip(lport_id, fip_id) mock_get_one.return_value = None self.assertIsNone(self.controller.update_floatingip(fip)) mock_get_one.assert_called_once_with(l2.LogicalPort(id=lport_id)) mock_get_fip.return_value = None fip.get_lport_id.return_value = None self.assertIsNone(self.controller.update_floatingip(fip)) mock_get_fip.assert_called_once_with(fip_id) mock_get_one.return_value = mock.Mock() fip.get_lport_id.return_value = lport_id self.assertIsNone(self.controller.update_floatingip(fip)) mock_assoc.assert_called_once_with(fip) old_fip = mock.Mock() mock_get_fip.return_value = old_fip mock_is_valid.return_value = False self.assertIsNone(self.controller.update_floatingip(fip)) mock_is_valid.assert_called_once() mock_is_valid.return_value = True self.controller.update_floatingip(fip) mock_update.assert_called_once_with(old_fip, fip)
def add_router_interface(self, context, router_id, interface_info): router_port_info = super(DFL3RouterPlugin, self).add_router_interface( context, router_id, interface_info) router = self.get_router(context, router_id) port = self.core_plugin.get_port(context, router_port_info['port_id']) subnet = self.core_plugin.get_subnet(context, router_port_info['subnet_id']) cidr = netaddr.IPNetwork(subnet['cidr']) network = "%s/%s" % (port['fixed_ips'][0]['ip_address'], str(cidr.prefixlen)) logical_port = self.nb_api.get( l2.LogicalPort(id=port['id'], topic=port['tenant_id'])) logical_router_port = neutron_l3.build_logical_router_port( router_port_info, mac=port['mac_address'], network=network, unique_key=logical_port.unique_key) lrouter = self.nb_api.get( l3.LogicalRouter(id=router_id, topic=router['tenant_id'])) lrouter.version = router['revision_number'] lrouter.add_router_port(logical_router_port) self.nb_api.update(lrouter) return router_port_info
def _get_another_local_lport(self): fake_local_port = l2.LogicalPort( id='fake_port2', topic='fake_tenant1', name='', unique_key=5, version=2, ips=[ netaddr.IPAddress('10.0.0.10'), netaddr.IPAddress('2222:2222::2') ], subnets=['fake_subnet1'], macs=[netaddr.EUI('fa:16:3e:8c:2e:12')], chassis='fake_host', lswitch='fake_switch1', security_groups=['fake_security_group_id1'], allowed_address_pairs=[], port_security_enabled=True, device_owner='compute:None', device_id='fake_device_id', ofport=20, is_local=True, # 'binding_profile': {}, # 'binding_vnic_type': 'normal', ) fake_local_port.is_local = True fake_local_port.ofport = 20 return fake_local_port
def test_disassociate_floatingip(self): with self._prepare_ext_net() as external_network_id: router = self.store( objects.RouterTestObj(self.neutron, self.nb_api)) fip = self.store( objects.FloatingipTestObj(self.neutron, self.nb_api)) router_para = { 'name': 'myrouter1', 'admin_state_up': True, 'external_gateway_info': {"network_id": external_network_id}} router.create(router=router_para) self.assertTrue(router.exists()) # private network private_network = self.store( objects.NetworkTestObj(self.neutron, self.nb_api)) private_network_id = private_network.create() self.assertTrue(private_network.exists()) # private subnet priv_subnet = self.store(objects.SubnetTestObj( self.neutron, self.nb_api, private_network_id, )) private_subnet_para = {'cidr': '10.0.0.0/24', 'ip_version': 4, 'network_id': private_network_id} priv_subnet_id = priv_subnet.create(private_subnet_para) self.assertTrue(priv_subnet.exists()) router_interface = router.add_interface(subnet_id=priv_subnet_id) router_lport = self.nb_api.get( l2.LogicalPort(id=router_interface['port_id'])) self.assertIsNotNone(router_lport) port = self.store( objects.PortTestObj(self.neutron, self.nb_api, private_network_id)) port_id = port.create() self.assertIsNotNone(port.get_logical_port()) fip_para = {'floating_network_id': external_network_id, 'port_id': port_id} # create fip.create(fip_para) self.assertTrue(fip.exists()) # disassociate with port fip.update({}) fip_obj = fip.get_floatingip() self.assertIsNone(fip_obj.lport) fip.close() self.assertFalse(fip.exists()) port.close() self.assertFalse(port.exists()) router.close() self.assertFalse(router.exists()) priv_subnet.close() self.assertFalse(priv_subnet.exists())
def delete_floatingip(self, floatingip): self.local_floatingips.pop(floatingip.get_id(), 0) lport = self.db_store2.get_one( l2.LogicalPort(id=floatingip.get_lport_id())) mac = lport.mac self.floatingip_rarp_cache.pop(mac, None) self._remove_ingress_nat_rules(floatingip) self._remove_egress_nat_rules(floatingip)
def _delete_subports(self, trunk, subports): """ Remove the subports that were deleted on the Neutron side from the Dragonflow NB DB """ df_parent = self.nb_api.get(l2.LogicalPort(id=trunk.port_id)) for subport in subports: self._delete_subport(trunk, subport, df_parent)
def _add_subports(self, trunk, subports): """ Create the subports that were created on the Neutron side in the Dragonflow NB DB """ df_parent = self.nb_api.get(l2.LogicalPort(id=trunk.port_id)) for subport in subports: self._add_subport(trunk, subport, df_parent)
def _get_ports_by_chassis(self, chassis): return self.db_store.get_all( l2.LogicalPort(binding=l2.PortBinding( type=l2.BINDING_CHASSIS, chassis=chassis.id, ), ), index=l2.LogicalPort.get_index('chassis_id'), )
def _get_vm_gateway_info(self, floatingip): lport = self.db_store2.get_one( l2.LogicalPort(id=floatingip.get_lport_id())) lrouter = self.db_store2.get_one(l3.LogicalRouter( id=floatingip.get_lrouter_id())) for router_port in lrouter.ports: if router_port.lswitch.id == lport.lswitch.id: return router_port.mac return None
def _create_child_port(self): return l2.LogicalPort(id='lport2', ips=['192.168.18.3'], subnets=['subnet2'], macs=['fa:16:3e:00:00:01'], enabled=True, lswitch='lswitch2', topic='fake_tenant1', unique_key=33)
def _get_vm_port_info(self, floatingip): lport = self.db_store2.get_one( l2.LogicalPort(id=floatingip.get_lport_id())) mac = lport.mac ip = lport.ip tunnel_key = lport.unique_key local_network_id = lport.local_network_id return mac, ip, tunnel_key, local_network_id
def test_delete_router_interface_port(self): router = self.store(objects.RouterTestObj(self.neutron, self.nb_api)) network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api)) network_id = network.create() self.assertTrue(network.exists()) subnet = self.store( objects.SubnetTestObj( self.neutron, self.nb_api, network_id, )) subnet_id = subnet.create({ 'cidr': '91.126.188.0/24', 'ip_version': 4, 'network_id': network_id }) router_id = router.create() self.assertTrue(router.exists()) interface_msg = {'subnet_id': subnet_id} router_l = self.neutron.add_interface_router(router_id, body=interface_msg) routers = self.nb_api.get_all(l3.LogicalRouter) router2 = None for r in routers: if r.id == router_l['id']: router2 = r break self.assertIsNotNone(router2) interface_port = self.neutron.show_port(router_l['port_id']) self.assertRaises(n_exc.Conflict, self.neutron.delete_port, interface_port['port']['id']) self.assertIsNotNone( self.nb_api.get(l2.LogicalPort(id=interface_port['port']['id']))) self.neutron.remove_interface_router(router.router_id, body=interface_msg) port2 = self.nb_api.get( l2.LogicalPort(id=interface_port['port']['id'])) self.assertIsNone(port2) subnet.close() router.close() network.close() self.assertFalse(router.exists()) self.assertFalse(network.exists())
def delete_chassis(self, chassis): LOG.info("Deleting remote ports in remote chassis %s", chassis.id) # Chassis is deleted, there is no reason to keep the remote port # in it. index = l2.LogicalPort.get_index('chassis_id') remote_ports = self.db_store.get_all(l2.LogicalPort(chassis=chassis), index=index) for port in remote_ports: self._delete_lport_instance(port) self.db_store.delete(chassis)
def _get_detect_items(self): items = [] for target_ip, refs in self.allowed_address_pairs_refs_list.items(): for lport_id in refs: lport = self.db_store.get_one(l2.LogicalPort(id=lport_id)) if lport is not None: items.append((target_ip, lport)) return items