"""
This module exposes an api to deal with db operations on no-sql databases.
Currently couchdb support is included.
"""
import couchdb
COUCHDB = 'couchdb'
LOCALHOST = 'localhost'
[docs]def todict(conn_string, kwargs):
"""Converts the input connection string into a dictionary.
Assumption: Connection string is of the format
'driver=couchdb;server=localhost;uid=database_uid;pwd=database_pwd'
"""
ret = {}
conn_dict = {}
for c_str in conn_string:
arr = c_str.split(';')
for elem in arr:
temp = elem.split('=')
ret[temp[0]] = temp[1]
conn_dict = ret.copy()
conn_dict.update(kwargs)
return conn_dict
[docs]class NsOdbc(object):
"""An abstraction layer to make api calls to a non relational database.
Currently the API provided is:
connect
create
get_doc
insert_update_doc
delete_doc
"""
def __init__(self):
self.version = '1.1'
self.conn = None
[docs] def connect(self, *conn_string, **kwargs):
"""Returns a connection object required for further operations
Input: connection string or connection parameters
Returns: connection object
"""
conn_dict = {}
conn_dict = todict(conn_string, kwargs)
# couchdb specific block.
if conn_dict['driver'] == COUCHDB:
auth_pair = (conn_dict['uid'], conn_dict['pwd'])
if conn_dict['server'] == LOCALHOST:
cnxn = ConnectionCouch(couchdb.Server(), auth_pair)
else:
server_pair = "http://{0}:{1}/".format(
conn_dict['server'], conn_dict['port'])
cnxn = ConnectionCouch(couchdb.Server(server_pair), auth_pair)
self.conn = cnxn
return cnxn
[docs] def get_attributes(self):
"""Returns API version"""
return self.version
[docs]class ConnectionCouch(object):
"""Connection class.
This class is specific to couchdb operations.
For others a new class will be needed (following same standards)
"""
def __init__(self, conn, credentials):
self.conn = conn
self.conn.resource.credentials = credentials
self.database = {}
[docs] def create(self, db_name):
"""Create a database.
If the database exists, return the same and send a True flag.
This way, a connection object will only be created once.
"""
# TODO: initial connection failure not handled
try:
self.database[db_name] = DatabaseCouch(self.conn.create(db_name))
return self.database[db_name], False
except couchdb.http.PreconditionFailed:
self.database[db_name] = DatabaseCouch(self.conn[db_name])
return self.database[db_name], True
[docs] def connected_databases(self):
"""
Return the connected databases of this connection
"""
return self.database
[docs] def delete(self, db_name):
"""
Delete database specified in the parameter
"""
self.conn.delete(db_name)
[docs]class DatabaseCouch(object):
"""Database specific class exposing the API.
"""
def __init__(self, database):
self.database = database
[docs] def insert_update_doc(self, doc, update_key=''):
"""Insert or update a document
For updating, a key has to be provided against
which a document will be updated
"""
try:
doc_id, _ = self.database.save(doc)
return doc_id
except couchdb.http.ResourceConflict:
l_doc = self.database.get(doc['_id'])
l_doc[update_key] = doc[update_key]
doc_id, _ = self.database.save(l_doc)
return doc_id
[docs] def get_docs(self, view_url, key):
"""Select docs
A view url is used as select query with the key as a where condition
"""
view_results = self.database.view(view_url, key=key)
return view_results.rows
[docs] def delete_doc(self, doc_id):
"""
Delete document based on the doc id
"""
doc = self.database.get(doc_id)
self.database.delete(doc)
[docs] def create_view(self, design, views):
"""
This is a couchdb functionality. Helps in creating views needed
for querying the database.
Input: Design name, view definition
"""
doc = {}
doc["_id"] = "_design/" + design
doc["language"] = "javascript"
doc["views"] = views
self.database.save(doc)
[docs]def nsodbc_factory():
"""factory method to consume the API"""
return NsOdbc()
[docs]def init_flow_db(flow_database):
"""
Initialize/Refresh flow database
Args:
flow_database
"""
views = {}
views["flow"] = {}
views["flow"]["map"] = "function(doc) " + \
"{\n emit(doc._id, doc);\n}"
views["match"] = {}
views["match"]["map"] = "function(doc) " + \
"{\nif(doc.data.OFPFlowStats.match)" + \
"{\n emit(" + \
"[doc.data.OFPFlowStats.table_id, " + \
"doc.data.OFPFlowStats.match], " + \
"doc );\n}\n}"
flow_database.create_view("flows", views)
[docs]def init_switch_db(switch_database):
"""
Initialize/refresh switch database
Args:
switch_database
"""
views = {}
views["switch"] = {}
views["switch"]["map"] = "function(doc) " + \
"{\n emit(doc._id, doc);\n}"
switch_database.create_view("switches", views)