liv2hak
liv2hak

Reputation: 15010

query from sqlalchemy returns AttributeError: 'NoneType' object

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)

When I run the above code I get the following error:

The problem that I am facing is for some reason if I use

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():

The querying from the database

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)

is giving the following error:

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

Upvotes: 4

Views: 9290

Answers (1)

Francis Avila
Francis Avila

Reputation: 31651

This line:

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

Is always true. The reason is that scalar() returns None only if there are no rows. However your query looks like SELECT EXISTS (SELECT * FROM source_to_port WHERE source_to_port.src_address=?). This will always return exactly one row with one column. The result will thus be True or False, never None.

Moving on to the line before the line that throws your exception: first() returns None if there are no matches, so q_res is None. Since q_res is None, q_res.port_no on the next line raises an exception.

(Note you can use one() if you want an exception to be thrown if there is no match.)

If you are expecting a match, double-check your data and your filter_by() condition to make sure they are doing what you think they should.

However I recommend that you use one query instead of two using first() or one(). With first(), you branch based on q_res being None or not:

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

Or with one(), you put your "flooding" branch in an exception handler:

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

A difference between these two approaches is that one() will ensure there is one and only one result, whereas first() doesn't care if there are multiple results.

Upvotes: 4

Related Questions