SQLAlchemy查询返回AttributeError:'NoneType'对象

4
from pox.core import core
import pox.openflow.libopenflow_01 as of
import re
import datetime

from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, Date, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql.expression import exists

log = core.getLogger()

engine = create_engine('sqlite:///nwtopology.db', echo=False)
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()

########################################################################
class SourcetoPort(Base):
    """"""
    __tablename__ = 'source_to_port'
    id = Column(Integer, primary_key=True)
    port_no        = Column(Integer)
    src_address    = Column(String,index=True)

    #----------------------------------------------------------------------
    def __init__(self, src_address,port_no):
        """"""
        self.src_address = src_address    
    self.port_no     = port_no

########################################################################

#create tables
Base.metadata.create_all(engine)

class Tutorial (object):
  def __init__ (self, connection):
    self.connection = connection
    connection.addListeners(self)
    # Use this table to keep track of which ethernet address is on
    # which switch port (keys are MACs, values are ports).
    self.mac_to_port = {} 
    self.matrix={} 

    #This will keep track of the traffic matrix. 
    #matrix[i][j]=number of times a packet from i went to j

  def send_packet (self, buffer_id, raw_data, out_port, in_port):
    #print "calling send_packet"
    #Sends a packet out of the specified switch port.
    msg = of.ofp_packet_out()
    msg.in_port = in_port
    msg.data = raw_data
    # Add an action to send to the specified port
    action = of.ofp_action_output(port = out_port)
    msg.actions.append(action)
    # Send message to switch
    self.connection.send(msg)

  def act_like_hub (self, packet, packet_in):
    #flood packet on all ports
    self.send_packet(packet_in.buffer_id, packet_in.data,
                     of.OFPP_FLOOD, packet_in.in_port)

  def act_like_switch (self, packet, packet_in):
    """
    Implement switch-like behavior.
    """
    # Learn the port for the source MAC
    #print "RECIEVED FROM PORT ",packet_in.in_port , "SOURCE ",packet.src
    # create a Session
    #Session = sessionmaker(bind=engine)
    #session = Session()
    self.mac_to_port[packet.src]=packet_in.in_port
    #if self.mac_to_port.get(packet.dst)!=None:
    #print "count for dst",session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).count(),str(packet.dst)
    #if session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).count():
    if session.query(exists().where(SourcetoPort.src_address == str(packet.dst))).scalar() is not None:
           #send this packet
       print "got info from the database"
       q_res = session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).one()
       self.send_packet(packet_in.buffer_id, packet_in.data,q_res.port_no, packet_in.in_port)
           #create a flow modification message
           msg = of.ofp_flow_mod()
           #set the fields to match from the incoming packet
       msg.match = of.ofp_match.from_packet(packet)
           #send the rule to the switch so that it does not query the controller again.
           msg.actions.append(of.ofp_action_output(port=q_res.port_no))
           #push the rule
           self.connection.send(msg)
    else:
           #flood this packet out as we don't know about this node.
           print "flooding the first packet"
           self.send_packet(packet_in.buffer_id, packet_in.data,
                       of.OFPP_FLOOD, packet_in.in_port)
       #self.matrix[(packet.src,packet.dst)]+=1  
       entry = SourcetoPort(src_address=str(packet.src) , port_no=packet_in.in_port)
           #add the record to the session object
           session.add(entry)
           #add the record to the session object
           session.commit()

  def _handle_PacketIn (self, event):
    """
    Handles packet in messages from the switch.
    """
    packet = event.parsed # This is the parsed packet data.
    if not packet.parsed:
      log.warning("Ignoring incomplete packet")
      return
    packet_in = event.ofp # The actual ofp_packet_in message.

    #self.act_like_hub(packet, packet_in)
    self.act_like_switch(packet, packet_in)

def launch ():
  """
  Starts the component
  """
  def start_switch (event):
    log.debug("Controlling %s" % (event.connection,))
    Tutorial(event.connection)
  core.openflow.addListenerByName("ConnectionUp", start_switch)

当我运行以上代码时,我遇到了以下错误:
我面临的问题是,由于某种原因,如果我使用

标签,代码就会出现问题。
if session.query(exists().where(SourcetoPort.src_address == str(packet.dst))).scalar() is not None:

in place of count query.

#if session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).count():

从数据库中查询

q_res = session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).first()
self.send_packet(packet_in.buffer_id, packet_in.data,q_res.port_no, packet_in.in_port)

出现以下错误:

DEBUG:core:POX 0.1.0 (betta) going up...
DEBUG:core:Running on CPython (2.7.3/Aug 1 2012 05:14:39)
DEBUG:core:Platform is Linux-3.5.0-23-generic-x86_64-with-Ubuntu-12.04-precise
INFO:core:POX 0.1.0 (betta) is up.
DEBUG:openflow.of_01:Listening on 0.0.0.0:6633
INFO:openflow.of_01:[00-00-00-00-00-02 1] connected
DEBUG:tutorial:Controlling [00-00-00-00-00-02 1]
got info from the database
ERROR:core:Exception while handling Connection!PacketIn...
Traceback (most recent call last):
  File "/home/karthik/pox/pox/lib/revent/revent.py", line 234, in raiseEventNoErrors
    return self.raiseEvent(event, *args, **kw)
  File "/home/karthik/pox/pox/lib/revent/revent.py", line 281, in raiseEvent
    rv = event._invoke(handler, *args, **kw)
  File "/home/karthik/pox/pox/lib/revent/revent.py", line 159, in _invoke
    return handler(self, *args, **kw)
  File "/home/karthik/pox/tutorial.py", line 118, in _handle_PacketIn
    self.act_like_switch(packet, packet_in)
  File "/home/karthik/pox/tutorial.py", line 86, in act_like_switch
    self.send_packet(packet_in.buffer_id, packet_in.data,q_res.port_no, packet_in.in_port)
AttributeError: 'NoneType' object has no attribute 'port_no'
got info from the database
ERROR:core:Exception while handling Connection!PacketIn...

2
你的代码缩进有问题。你混用了制表符和空格吗? - Francis Avila
1个回答

4

这一行:

if session.query(exists().where(SourcetoPort.src_address == str(packet.dst))).scalar() is not None:

始终为真。原因是scalar()仅在没有行时返回None。然而,您的查询看起来像SELECT EXISTS (SELECT * FROM source_to_port WHERE source_to_port.src_address=?)。这将始终返回一个具有一个列的行。结果因此将是TrueFalse,从不是None
继续到抛出异常的那一行之前的那一行:first()如果没有匹配,则返回None,因此q_resNone。由于q_resNone,所以下一行的q_res.port_no会引发异常。
(请注意,如果要抛出异常,则可以使用one()。)
如果您期望有匹配,请仔细检查数据和filter_by()条件,以确保它们正在执行您认为应该执行的操作。
但是,我建议您使用一个查询而不是使用first()one()两个查询。使用first()时,您将根据q_res是否为None进行分支:
q_res = session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).first()
if q_res is not None:
    print "got info from the database"
    self.send_packet(....)
    ...
else:
    print "flooding the first packet"
    ...

或者使用 one(),将“flooding”分支放入异常处理程序中:

from sqlalchemy.orm.exc import (NoResultFound, MultipleResultsFound)

try:
    q_res = session.query(SourcetoPort).filter_by(src_address=str(packet.dst)).one()
except NoResultFound:
    print "flooding the first packet"
    ...
# except MultipleResultsFound:
#     print "More than one result found! WUT?!"
else:
    print "got info from the database"
    ...

这两种方法的区别在于one()会确保只有一个结果,而first()则不关心是否有多个结果。

它应该仅在不为none时进入该循环。所以我不明白为什么它返回none。我已经尝试使用first(),它也返回none。 - liv2hak
1
SELECT EXISTS (..) 的结果将为 TrueFalse,从不是 None,因为此查询始终返回具有一行的结果。您的条件将始终为真。 - Francis Avila
1
@liv2hak 我扩展了答案,说明了我认为你应该如何做到这一点(只使用一个查询而不是两个)。 - Francis Avila

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接