来自sqlalchemy的查询返回AttributeError:'NoneType'对象

时间:2013-05-02 22:04:56

标签: python python-2.7 sqlalchemy

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...

1 个答案:

答案 0 :(得分: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()并不关心是否存在多个结果。< / p>