I am trying to set up an API using Falcon and SQLAlchemy using a postgresql database. Using gunicorn to start the API
I am getting the following error when trying to access /v1/users.
I tried to browse the SQLAlchemy documentation but could not find any working solution.
Thanks
[2017-03-15 10:20:26 +0100] [20516] [ERROR] Error handling request /v1/users
Traceback (most recent call last):
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 135, in handle
self.handle_request(listener, req, client, addr)
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 176, in handle_request
respiter = self.wsgi(environ, resp.start_response)
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/falcon/api.py", line 209, in __call__
responder(req, resp, **params)
File "/Users/juliencourtes/Documents/projects/FalconAPI/app/api/v1/users.py", line 50, in on_get
users = session.query(User).all()
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py", line 157, in do
return getattr(self.registry(), name)(*args, **kwargs)
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 1330, in query
return self._query_cls(entities, self, **kwargs)
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 139, in __init__
self._set_entities(entities)
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 148, in _set_entities
entity_wrapper(self, ent)
File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 3947, in __init__
"expected - got '%r'" % (column, )
sqlalchemy.exc.InvalidRequestError: SQL expression, column, or mapped entity expected - got '<function User at 0x1040216a8>'
My projects files
main.py
import falcon
from app.api.v1 import users
from app.middleware import DatabaseSessionManager
from app.database import db_session, init_session
from app.api.common import base
class App(falcon.API):
def __init__(self,*args, **kwargs):
super(App, self).__init__(*args, **kwargs)
self.add_route('/',base.BaseResource())
self.add_route('/v1/users',users.Collection())
init_session()
mdlw = [DatabaseSessionManager(db_session)]
application = App(middleware=mdlw)
session.py
import sqlalchemy.orm.scoping as scoping
from sqlalchemy.exc import SQLAlchemyError
from app import config
class DatabaseSessionManager(object):
def __init__(self, db_session):
self._session_factory = db_session
self._scoped = isinstance(db_session, scoping.ScopedSession)
def process_request(self, req, res, resource=None):
req.context['session'] = self._session_factory
def process_response(self, req, res, resource=None):
session = req.context['session']
if config.DB_AUTOCOMMIT:
try:
session.commit()
except SQLAlchemyError as ex:
session.rollback()
if self._scoped:
session.remove()
else:
session.close()
database init.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
import psycopg2
from app import config
db_session = scoped_session(sessionmaker())
engine = create_engine('postgresql+psycopg2://xxxxx#localhost/falcon_api')
def init_session():
db_session.configure(bind=engine)
user.py
from sqlalchemy import Column
from sqlalchemy import String, Integer,Text
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
def User(Base):
__tablename__ = 'user'
id = Column('id', UUID, primary_key=True)
firstname = Column('firstname', String)
lastname = Column('lastname', String)
#It tells python how to print the class, used for debugging
def __repr__(self):
return "<User(id='%s', name='%s', lastname='%s')>"% \
(self.id, self.firstname, self.lastname)
def __init__(self, id , firstname, lastname):
self.id = id
self.firstname = firstname
self.lastname = lastname
api/v1/users.py
import falcon
import json
from sqlalchemy.ext.declarative import declarative_base
try:
from collections import OrderedDict
except ImportError:
OrderedDict = dict
from app.models import User
class Collection():
"""
Handle for endpoint: /v1/users
"""
def to_json(self, body_dict):
return json.dumps(body_dict)
def on_error(self, resp, error=None):
resp.status = error['status']
meta = OrderedDict()
meta['code'] = error['code']
meta['message'] = error['message']
obj = OrderedDict()
obj['meta'] = meta
resp.body = self.to_json(obj)
def on_success(self, resp, data=None):
resp.status = falcon.HTTP_200
meta = OrderedDict()
meta['code'] = 200
meta['message'] = 'OK'
obj = OrderedDict()
obj['meta'] = meta
obj['data'] = data
resp.body = self.to_json(obj)
def on_get(self, req, resp):
session = req.context['session']
#Bugging here
users = session.query(User).all()
I have faced same problem..It got fixed in my case for the following changes..
instead session.remove() use the following code.
if self._scoped:
self._session_factory.remove()
This will work.
We create models for SQL in python using Class which inherits built in class from ORM like SQLAlchemy. Here you are trying to create User model. But as it need to be a class to work properly instead you declared it as a function in user.py.
All you have to do to make it work is just change def User to class User.
So user.py will look something like:
...
class User(Base):
__tablename__ = 'user'
...
Related
When querying a table, I'd like to eager load a set of columns. Prior to 1.3.x, I could do it with the below code, but it now I'm getting:
sqlalchemy.exc.ArgumentError: Attribute "AliasedClass_Blueprint.engineer" does not link from element "mapped class
Blueprint->blueprints". Did you mean to use Building.blueprint.of_type(AliasedClass_Blueprint)?
The query in question is setup as follows:
def doQuery():
building = aliased(Building)
blueprint = aliased(Blueprint, name="blueprint")
engineer = aliased(Engineer, name="engineer")
with sessionScope() as session:
return session.query(building)\
.join(blueprint, blueprint.id==building.blueprintId)\
.outerjoin(engineer, blueprint.engineerId==engineer.id)\
.options(contains_eager(building.blueprint, alias=blueprint))\
.options(contains_eager(building.blueprint, alias=blueprint)\
# The error shows up here
.contains_eager(blueprint.engineer, alias=engineer))
The error suggests using building.blueprint.of_type(blueprint), which seems to work properly, but it looks to be accomplishing something similar to what alias=blueprint does. Does of_type(x) replace the alias=x parameter?
Below is a functioning example with both a working function and one that reproduces the error:
from sqlalchemy import create_engine, inspect, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, aliased, contains_eager
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Engineer(Base):
__tablename__ = "engineers"
id=Column(Integer, primary_key=True)
name=Column(String)
def __repr__(self):
return self.name
class Blueprint(Base):
__tablename__ = "blueprints"
id=Column(Integer, primary_key=True)
name=Column(String)
engineerId=Column(Integer, ForeignKey('engineers.id'))
engineer=relationship(Engineer, foreign_keys=[engineerId], backref="outputBlueprints")
def __repr__(self):
return self.name
class Building(Base):
__tablename__ = "buildings"
id=Column(Integer, primary_key=True)
name=Column(String)
blueprintId=Column(Integer, ForeignKey('blueprints.id'))
blueprint = relationship(Blueprint, foreign_keys=[blueprintId], remote_side=[Blueprint.id], backref='buildings')
def __repr__(self):
return self.name
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
sessionFactory = sessionmaker(bind=engine, expire_on_commit=False)
#contextmanager
def sessionScope():
try:
session = sessionFactory()
yield session
session.commit()
except Exception as e:
session.rollback()
raise
finally:
session.close()
return
def doQueryWorking():
building = aliased(Building)
blueprint = aliased(Blueprint, name="blueprint")
engineer = aliased(Engineer, name="engineer")
with sessionScope() as session:
return session.query(building)\
.join(blueprint, blueprint.id==building.blueprintId)\
.outerjoin(engineer, blueprint.engineerId==engineer.id)\
.options(contains_eager(building.blueprint.of_type(blueprint)))\
.options(contains_eager(building.blueprint.of_type(blueprint))\
.contains_eager(blueprint.engineer, alias=engineer))
def doQueryError():
building = aliased(Building)
blueprint = aliased(Blueprint, name="blueprint")
engineer = aliased(Engineer, name="engineer")
with sessionScope() as session:
return session.query(building)\
.join(blueprint, blueprint.id==building.blueprintId)\
.outerjoin(engineer, blueprint.engineerId==engineer.id)\
.options(contains_eager(building.blueprint, alias=blueprint))\
.options(contains_eager(building.blueprint, alias=blueprint)\
.contains_eager(blueprint.engineer, alias=engineer))
buildings = doQueryError()
I'm currently developing an API for AWS with Chalice in Python that uses Pony ORM to handle our database. When trying to query with a select like this db.select(s.start_time for s in db.Session) I'm getting the 'Expected string or bytes-like object" error (full stack-trace below). However querying using a lambda like this db.Session.select(lambda s: s.id = 3) works as expected. I'm at a loss to what could be causing it, a guess would be that the db.Provider part isn't liked when generating, but I'm not sure what Pony expects there. I've tried debugging with pdb, but I'm not sure what it's telling me.
Stack trace:
Traceback (most recent call last):
File "c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\chalice\app.py", line 842, in _get_view_function_response
response = view_function(**function_args)
File "C:\Users\Gamer\Documents\AWS-SakMed\backend\SakMed\app.py", line 51, in _view_function
return wrapped(*args, **kwargs)
File "", line 2, in get_cases
File "c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py", line 528, in new_func
result = func(*args, **kwargs)
File "C:\Users\Gamer\Documents\AWS-SakMed\backend\SakMed\app.py", line 89, in get_cases
query = db.select(p.first_name for p in db.Provider)
File "c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py", line 881, in select
if not select_re.match(sql): sql = 'select ' + sql
TypeError: expected string or bytes-like object
Stepping through pdb debug (formatting is a bit weird):
c:\users\gamer\documents\aws-sakmed\backend\sakmed\app.py(89)get_cases()
-> query = db.select(p.first_name for p in db.Provider) (Pdb) step(s)
--Call-- c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(3927)iter()
-> def iter(entity): (Pdb) c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(3928)iter()
-> return EntityIter(entity) (Pdb)
--Call-- c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(3630)init()
-> def init(self, entity): (Pdb) c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(3631)init()
-> self.entity = entity (Pdb)
--Return-- c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(3631)init()->None
-> self.entity = entity (Pdb)
--Return-- c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(3928)iter()->
-> return EntityIter(entity) (Pdb)
--Call-- c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(879)select()
-> #cut_traceback (Pdb) c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(881)select()
-> if not select_re.match(sql): sql = 'select ' + sql (Pdb) pp(sql) generator object get_cases.locals.genexpr at 0x048062B0 (Pdb)
step(s) TypeError: expected string or bytes-like object
c:\users\gamer.virtualenvs\backend-qptpobgm\lib\site-packages\pony\orm\core.py(881)select()
-> if not select_re.match(sql): sql = 'select ' + sql (Pdb)
Relevant code: app.py
db = create_database()
datastore = DataStore(db)
def app_db_session(func):
wrapped = db_session(func)
def _view_function(*args, **kwargs):
global db_is_bound
if not db_is_bound:
debug = os.getenv('localdev')
if debug is None:
datastore.connect(host, name, password, dbname)
elif debug == 'True':
datastore.connect('localhost', 'user', 'password', 'local-db')
db_is_bound = True
return wrapped(*args, **kwargs)
return _view_function
#app.route('/recipient/{rec_id}/cases', methods=['GET'])
#app_db_session
def get_cases(rec_id):
query = db.Provider.select(lambda p: p.id == 1)
query = db.select(p.first_name for p in db.Provider))
Relevant code: data_store.py
class DataStore():
def __init__(self, db):
self.db = db
def connect(self, host, user, passwd, db_name):
self.db.bind(provider='mysql', host=host, user=user, passwd=passwd, db=db_name)
self.__map_data_models()
def bind_memory(self):
self.db.bind(provider='sqlite', filename=':memory:')
self.__map_data_models()
def __map_data_models(self):
self.db.generate_mapping(create_tables=True)
Relevant code: base.py
def create_database():
db = Database()
class Provider(db.Entity):
id = PrimaryKey(int, auto=True)
hsa_id = Required(str)
role = Optional(str)
available = Required(bool)
first_name = Optional(str)
last_name = Optional(str)
return db
If you want to use generator syntax, you need to use select function:
from pony import orm
...
query = orm.select(p for p in Person if p.name.startswith('A'))
for obj in query:
print(obj.name)
The method select of Database object is used for raw SQL queries
from pony import orm
...
db = orm.Database('sqlite', ':memory:')
...
rows = db.select("id, name FROM person p WHERE p.name LIKE 'A%'")
for row in rows:
print(row[1])
I am new to python and flask, trying to import a function from route_user.py file which is present in folder named Users_service into a route_jd.py file which is present in folder named JD_service and both the folder Users_service and JD_service is present inside a folder named blue.
route_users.py file
from flask import jsonify,request,Blueprint,make_response
from flask_pymongo import PyMongo
from blue import app
from werkzeug.security import generate_password_hash,check_password_hash
import uuid
import jwt
import datetime
from functools import wraps
app.config['SECRET_KEY'] = 'itshouldbehidden'
app.config['MONGO_URI'] = "mongodb://localhost:27017/mydata"
mongo = PyMongo(app)
mod = Blueprint('Users_Service',__name__)
def token_required(f):
#wraps(f)
def decorated(*args,**kwargs):
token = None
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message':'token is missing!'}),401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
print(data)
jd = mongo.db.hashed_User
current_user = jd.find_one({'public_id':data['public_id']})
print(current_user)
except:
return jsonify({'message':'Token is invalid'}),401
return f(current_user,*args,**kwargs)
return decorated
#mod.route('/add',methods=['POST'])
#token_required
def create_user(current_user):
if not current_user['admin']:
return jsonify({'message':'cannot perform that function'})
jd = mongo.db.hashed_User
data = request.get_json()
try:
hashed_password = generate_password_hash(data['password'],method='sha256')
jd.insert({"public_id":str(uuid.uuid4()),"name":data['name'],"password":hashed_password,"admin":False})
output = 'new user created!'
except:
output = 'please fill all the fields and try again'
return jsonify({'message':output})
#mod.route('/all',methods=['GET'])
#token_required
def get_all_users(current_user):
if not current_user['admin']:
return jsonify({'message':'cannot perform that function'})
jd = mongo.db.hashed_User
output = []
for q in jd.find():
output1={'User Name': q['name'],'Password':q['password'],'Public ID':q['public_id'],'Admin':q['admin']}
output.append(output1)
return jsonify({'Users':output})
#mod.route('/<public_id>',methods=['GET'])
#token_required
def get_one_user(current_user,public_id):
if not current_user['admin']:
return jsonify({'message':'cannot perform that function'})
jd = mongo.db.hashed_User
user = jd.find_one({'public_id':public_id})
if not user:
return jsonify({'message':'No such User exists'})
user_data={'User Name': user['name'],'Password':user['password'],'Public ID':user['public_id'],'Admin':user['admin']}
return jsonify({'Users':user_data})
#mod.route('/admin_promote/<public_id>',methods=['PUT'])
#token_required
def Promote_to_admin(current_user,public_id):
if not current_user['admin']:
return jsonify({'message':'cannot perform that function'})
jd = mongo.db.hashed_User
user = jd.find_one({'public_id':public_id})
if not user:
return jsonify({'message':'No such User exists'})
whereto = { "public_id":public_id}
newvalues={"$set":{'admin':True}}
jd.update_one(whereto,newvalues)
return jsonify({'message':'promoted to admin successfully'})
#mod.route('/delete/<public_id>',methods=['DELETE'])
#token_required
def delete_user(current_user,public_id):
if not current_user['admin']:
return jsonify({'message':'cannot perform that function'})
jd = mongo.db.hashed_User
user = jd.find_one({'public_id':public_id})
if not user:
return jsonify({'message':'No such User exists'})
jd = mongo.db.hashed_User
jd.delete_one({'public_id':public_id})
return jsonify({'message':'Deleted successfully'})
#mod.route('/auth/login',methods=['GET'])
def login():
auth=request.authorization
print(auth)
if not auth or not auth.username or not auth.password:
return make_response('Could not very',401,{'WWW-Authenticate':'Basic realm="Login required!"'})
jd = mongo.db.hashed_User
user = jd.find_one({'name':auth.username})
if not user:
return make_response('Could not very',401,{'WWW-Authenticate':'Basic realm="Login required!"'})
if check_password_hash(user['password'],auth.password):
token = jwt.encode({'public_id' : user['public_id'],'exp':datetime.datetime.utcnow() + datetime.timedelta(minutes=30)},app.config['SECRET_KEY'])
return jsonify({'token' : token.decode('UTF-8')})
return make_response('Could not very',401,{'WWW-Authenticate':'Basic realm="Login required!"'})
route_jd.py file
from flask import jsonify,request,Blueprint
from flask_pymongo import PyMongo
from blue import app
from blue.Users_Service import token_required # getting an error here
app.config['MONGO_URI'] = "mongodb://localhost:27017/mydata"
mongo = PyMongo(app)
mod = Blueprint('JD_Service',__name__)
#mod.route('/',methods=['GET'])
#token_required
def get_all_jds():
if not current_user['admin']:
return jsonify({'message':'cannot perform that function'})
jd = mongo.db.User11
output = []
.
.
.
.some more codes below
the error i m getting is
Traceback (most recent call last):
File "run.py", line 1, in <module>
from blue import app
File "D:\flask_resource\blue\__init__.py", line 5, in <module>
from blue.JD_Service.route_jd import mod
File "D:\flask_resource\blue\JD_Service\route_jd.py", line 4, in <module>
from blue.Users_Service import token_required
ImportError: cannot import name 'token_required'
as i m new to python and flask please help me out how to get rid of this error ?and what m i doing wrong?
Thanks in advance!
You forgot to put file name while importing in route_jd.py file:
from blue.Users_Service.route_users import token_required
Two ways:
Define Users_Service as a module, that is create a file __init__.py in folder Users_Service
Within __init__.py create import import * from blue.Users_Service.route_users
In route_jd.py: from blue.Users_Service import token_required
That is you are trying to import from module blue.Users_Service which in your case is not a module, but just a folder
Other way is to import directly:
from blue.Users_Service.route_users import token_required
Currently trying to create a basic blog using Google App Engine in Python. Here's the python code that I am using:
import os
import re
import webapp2
import jinja2
from string import letters
from google.appengine.ext import db
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir), autoescape=True)
class Handler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self, template, **params):
t = jinja_env.get_template(template)
return t.render(params)
def render(self, template, **kw):
self.write(self.render_str(template, **kw))
def post_key(name = "dad"):
return db.Key.from_path('blog', name)
class Blogger(db.Model):
name = db.StringProperty()
content = db.TextProperty()
created = db.DateTimeProperty(auto_now_add = True)
def render(self):
self._render_text = self.content.replace('\n', '<br>')
return render_str("post.html", p = self)
class MainPage(Handler):
def get(self):
self.response.write("Visit our blog")
class BlogHandler(Handler):
def get(self):
posts = db.GqlQuery("SELECT * FROM Blogger order by created desc")
self.render("frontblog.html", posts = posts)
class SubmitHandler(Handler):
def get(self):
self.render("temp.html")
def post(self):
name = self.request.get("name")
content = self.request.get("content")
if name and content:
a = Blogger(parent = post_key(), name = name, content = content)
a.put()
self.redirect('/blog/%s' % str(a.key().id()))
else:
error = "Fill in both the columns!"
self.render("temp.html", name = name, content = content, error = error)
class DisplayPost(Handler):
def get(self, post_id):
po = Blogger.get_by_id(post_id)
if po:
self.render("perma.html", po = po)
else:
self.response.write("404 Error")
app = webapp2.WSGIApplication([('/', MainPage),
('/blog', BlogHandler),
('/blog/submit', SubmitHandler)
('/blog/([0-9]+)', DisplayPost)], debug=True)
However, when I try to run this code on my local server, this is what I get as an error:
File "F:\Python 2.7\engineapp1\HelloApp\appapp\main.py", line 66, in <module>
('/blog/([0-9]+)', DisplayPost)], debug=True)
TypeError: 'tuple' object is not callable
What seems to be the problem here?
You forgot to add a comma.
('/blog/submit', SubmitHandler) <---- missed comma over here
('/blog/([0-9]+)', DisplayPost)], debug=True)
It's acting like a function in this case, you're passing a parameter to a tuple which result in an error that the tuple is not callable.
('/blog/submit', SubmitHandler)(parameter)
There's a missing comma at the line:
('/blog/submit', SubmitHandler)
It should be:
('/blog/submit', SubmitHandler),
Without the comma, you have ('/blog/submit', SubmitHandler)('/blog/([0-9]+)', DisplayPost), which is trying to call ('/blog/submit', SubmitHandler) as a function, with '/blog/([0-9]+)' and DisplayPost as parameters. Since that's not a function, but a tuple, you get that error.
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...
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.