~ubuntu-branches/ubuntu/utopic/gozerbot/utopic

« back to all changes in this revision

Viewing changes to build/lib/gozerbot/database/alchemy.py

  • Committer: Package Import Robot
  • Author(s): Jeremy Malcolm
  • Date: 2012-04-03 21:58:28 UTC
  • mfrom: (3.1.11 sid)
  • Revision ID: package-import@ubuntu.com-20120403215828-6mik0tzug5na93la
Tags: 0.99.1-2
* Removes logfiles on purge (Closes: #668767)
* Reverted location of installed files back to /usr/lib/gozerbot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# gozerbot/databse/alchemy.py
 
2
#
 
3
#
 
4
 
 
5
""" alchemy interface. """
 
6
 
 
7
__copyright__ = 'this file is in the public domain'
 
8
 
 
9
## IMPORT SECTION
 
10
 
 
11
# gozerbot imports
 
12
from gozerbot.stats import stats
 
13
from gozerbot.datadir import datadir
 
14
from gozerbot.config import config
 
15
from gozerbot.utils.locking import lockdec
 
16
from gozerbot.utils.log import rlog
 
17
from gozerbot.utils.exception import handle_exception
 
18
 
 
19
# sqlalchemy imports
 
20
from sqlalchemy.ext.declarative import declarative_base
 
21
from sqlalchemy.ext.associationproxy import association_proxy
 
22
from sqlalchemy.ext.orderinglist import ordering_list
 
23
from sqlalchemy import Text, Integer, Sequence, ForeignKey, DateTime
 
24
from sqlalchemy import create_engine, Column, String, Table
 
25
from sqlalchemy.orm import  scoped_session, sessionmaker, relation, eagerload
 
26
from sqlalchemy.orm import create_session as cs
 
27
 
 
28
# basic imports
 
29
import sqlalchemy, thread, os, time, logging
 
30
 
 
31
## END IMPORT
 
32
 
 
33
# debug settings
 
34
if config['debug']:
 
35
    logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
 
36
 
 
37
# locks
 
38
alchemylock = thread.allocate_lock()
 
39
alchemylocked = lockdec(alchemylock)
 
40
sessionlock = thread.allocate_lock()
 
41
sessionlocked = lockdec(sessionlock)
 
42
querylock = thread.allocate_lock()
 
43
querylocked = lockdec(querylock)
 
44
createlock = thread.allocate_lock()
 
45
createlocked = lockdec(createlock)
 
46
 
 
47
created = []
 
48
 
 
49
 
 
50
 
 
51
def geturi(ddir=None, mainconfig=None):
 
52
 
 
53
    """  determine database URI from config file """
 
54
 
 
55
    d = ddir or datadir
 
56
 
 
57
    # set config file
 
58
    if mainconfig:
 
59
        config = mainconfig 
 
60
    else:
 
61
        from gozerbot.config import config
 
62
 
 
63
    # if dburi not provided in config file construct it
 
64
    if not config['dburi']:
 
65
 
 
66
        if not 'sqlite' in config['dbtype'] and not 'mysql' in config['dbtype']:
 
67
            dburi = "%s://%s:%s@%s/%s" % (config['dbtype'], config['dbuser'], \
 
68
config['dbpasswd'], config['dbhost'], config['dbname'])
 
69
        elif 'mysql' in config['dbtype']:
 
70
            dburi = "%s://%s:%s@%s/%s?charset=utf8&use_unicode=0" % (config['dbtype'], config['dbuser'], \
 
71
config['dbpasswd'], config['dbhost'], config['dbname'])
 
72
        else:
 
73
            if not os.path.isdir(d + os.sep + 'db'):
 
74
                os.mkdir(d + os.sep + 'db')
 
75
            dburi = "sqlite:///%s/%s" % (ddir or datadir, config['dbname'])
 
76
 
 
77
    else:
 
78
        # dburi found in config
 
79
        dburi = config['dburi']
 
80
 
 
81
        # determine dbtype
 
82
        try:
 
83
            dbtype = dburi.split(':')[0]
 
84
        except:
 
85
            rlog(10, 'alchemy', "can't extract db data from dburi")
 
86
            dbtype = 'unknown'
 
87
 
 
88
        # save dbtype
 
89
        if config['dbtype'] != dbtype:
 
90
            config['dbtype'] = dbtype
 
91
            config.save()
 
92
 
 
93
    return dburi
 
94
 
 
95
def dbstart(ddir=None, mainconfig=None, base=None):
 
96
 
 
97
    """ start the database connection setting Session and engine. """
 
98
 
 
99
    dburi = geturi(ddir, mainconfig)
 
100
 
 
101
    # only show dburi if it doesn't contain a password
 
102
    if '///' in dburi:
 
103
        rlog(10, 'alchemy', 'starting database %s' % dburi)
 
104
    else:
 
105
        rlog(10, 'alchemy', 'starting database')
 
106
 
 
107
    try:
 
108
        engine = create_engine(dburi, strategy='threadlocal', pool_recycle=60, pool_size=50, max_overflow=0)
 
109
    except:
 
110
        engine = create_engine(dburi, strategy='threadlocal', pool_recycle=3600)
 
111
    #try:
 
112
    #    engine = create_engine(dburi, pool_recycle=60, pool_size=50, max_overflow=0)
 
113
    #except:
 
114
    #    engine = create_engine(dburi, pool_recycle=3600)
 
115
 
 
116
    # setup metadata and session
 
117
    if not base:
 
118
        base = Base
 
119
    base.metadata.bind = engine
 
120
    create_all()
 
121
    rlog(10, 'alchemy', 'done')
 
122
    Session = scoped_session(sessionmaker(bind=engine, autocommit=True))
 
123
    stats.up('alchemy', 'engines')
 
124
 
 
125
    return (Session, engine)
 
126
 
 
127
# vars
 
128
Base = declarative_base()
 
129
#Session, engine = dbstart(datadir)
 
130
Session = engine = None
 
131
 
 
132
def startmaindb(ddir=None, mainconfig=None):
 
133
 
 
134
    """ start the main database. """
 
135
 
 
136
    global Session
 
137
    global engine
 
138
 
 
139
    Session, engine = dbstart(ddir, mainconfig)
 
140
    #pass
 
141
 
 
142
def dblocked(func, *args, **kwargs):
 
143
 
 
144
    @alchemylocked
 
145
    def dofunc(*args, **kwargs):
 
146
        try:
 
147
            Session.begin(subtransactions=True)
 
148
            res = func(*args, **kwargs)
 
149
            Session.commit()
 
150
            try: idnr = res.indx
 
151
            except AttributeError: idnr = None
 
152
            Session.close()
 
153
            return idnr or res
 
154
        except Exception, ex:
 
155
            Session.rollback() ; Session.close()
 
156
            raise
 
157
    return dofunc
 
158
 
 
159
def create_all(plugname='all', base=None):
 
160
    rlog(10, 'alchemy', 'running create_all (%s)' % plugname)
 
161
    if plugname not in created:
 
162
        created.append(plugname)
 
163
        if not base:
 
164
            base = Base
 
165
        try: base.metadata.create_all()
 
166
        except Exception, ex: rlog(10, 'alchemy', 'problem creating %s tables: %s' % (str(base), str(ex)))
 
167
    else:
 
168
        rlog(10, 'alchemy', '%s tables already created' % plugname)
 
169
 
 
170
### MODEL
 
171
 
 
172
user = Table('user', Base.metadata,
 
173
    Column('name', String(255), primary_key=True)
 
174
)
 
175
 
 
176
email = Table('email', Base.metadata,
 
177
    Column('name', String(255), ForeignKey(user.c.name), nullable=False),
 
178
    Column('email', String(255), nullable=False),
 
179
    Column('order', Integer, nullable=False)
 
180
)
 
181
 
 
182
class User(Base):
 
183
    __table__ = user
 
184
    _userhosts = relation("UserHost", backref="user", cascade="all, delete-orphan")
 
185
    _perms = relation("Perms", backref="user", cascade="all, delete-orphan")
 
186
    _permits = relation("Permits", backref="user",cascade="all, delete-orphan" )
 
187
    _statuses = relation("Statuses", backref="user", cascade="all, delete-orphan")
 
188
    _pasword = relation("Passwords", backref="user", cascade="all, delete-orphan")
 
189
    _email = relation("Email", backref="user", collection_class=ordering_list('order'),
 
190
                        cascade="all, delete-orphan", order_by=[email.c.order])
 
191
    email = association_proxy('_email', 'email')
 
192
    userhosts = association_proxy('_userhosts', 'userhost')
 
193
    perms = association_proxy('_perms', 'perm')
 
194
    permits = association_proxy('_permits', 'permit')
 
195
    statuses = association_proxy('_statuses', 'status')
 
196
    password = association_proxy('_password', 'passwd')
 
197
 
 
198
class Email(Base):
 
199
    __table__ = email
 
200
    __mapper_args__ = {'primary_key':[email.c.name,email.c.email]}
 
201
 
 
202
    def __init__(self, email):
 
203
        self.email = email
 
204
 
 
205
class UserHost(Base):
 
206
    __tablename__ = 'userhosts'
 
207
    userhost = Column('userhost', String(255), primary_key=True)
 
208
    name = Column('name', String(255), ForeignKey('user.name'), nullable=False)
 
209
 
 
210
    def __init__(self, userhost):
 
211
        self.userhost = userhost
 
212
 
 
213
class Perms(Base):
 
214
    __tablename__ = 'perms'
 
215
    name = Column('name', String(255), ForeignKey('user.name'), nullable=False)
 
216
    perm = Column('perm', String(255), nullable=False)
 
217
    __mapper_args__ = {'primary_key':[name,perm]}
 
218
 
 
219
    def __init__(self, perm):
 
220
        self.perm = perm
 
221
 
 
222
class Permits(Base):
 
223
    __tablename__ = 'permits'
 
224
    name = Column('name', String(255), ForeignKey('user.name'), nullable=False)
 
225
    permit = Column('permit', String(255), nullable=False)
 
226
    __mapper_args__ = {'primary_key':[name,permit]}
 
227
 
 
228
    def __init__(self, permit):
 
229
        self.permit = permit
 
230
 
 
231
class Statuses(Base):    
 
232
    __tablename__ = 'statuses'
 
233
    name = Column('name', String(255), ForeignKey('user.name'), nullable=False)
 
234
    status = Column('status', String(255), nullable=False)
 
235
    __mapper_args__ = {'primary_key':[name,status]}
 
236
 
 
237
    def __init__(self, status):
 
238
        self.status = status
 
239
 
 
240
class Passwords(Base):    
 
241
    __tablename__ = 'passwords'
 
242
    name = Column('name', String(255), ForeignKey('user.name'), primary_key=True)
 
243
    passwd = Column('passwd', String(255), nullable=False)
 
244
 
 
245
    def __init__(self, passwd):
 
246
        self.passwd = passwd
 
247
 
 
248
### END MODEL
 
249
 
 
250
def query(q):
 
251
    """ do a query on the database. """
 
252
    stats.up('alchemy', 'query')
 
253
    res = Session.query(q)
 
254
    return res
 
255
 
 
256
def douser(userhost):
 
257
    user = query(UserHost).filter_by(userhost=userhost).first()
 
258
    if user:
 
259
        res = query(User).filter_by(name=user.name.lower()).first()
 
260
        if res: 
 
261
            return res
 
262
 
 
263
 
 
264
def getuser(userhost):
 
265
 
 
266
    """ get a user based on userhost. """
 
267
 
 
268
    stats.up('alchemy', 'getuser')
 
269
    try:
 
270
        return douser(userhost)
 
271
    except sqlalchemy.exc.OperationalError, ex:
 
272
        if 'server has gone away' in str(ex):
 
273
            rlog(10, 'alchemy', 'mysql server has gone away')
 
274
            startmaindb()
 
275
            return douser(userhost)
 
276
        else:
 
277
            raise
 
278
    except Exception, ex:
 
279
        rlog(10, 'alchemy', 'error getting %s user: %s' % (str(userhost), str(ex)))
 
280
 
 
281
def doname(name):
 
282
        res = Session.query(User).filter_by(name=name.lower()).first()
 
283
        return res
 
284
 
 
285
def byname(name):
 
286
    """ get a users based on name. """
 
287
    try:
 
288
        return doname(name)
 
289
    except Exception, ex:
 
290
        if 'server has gone away' in str(ex):
 
291
            rlog(10, 'alchemy', 'error: %s' % str(ex))
 
292
            startmaindb()
 
293
            return doname(name)
 
294
        else:
 
295
            raise
 
296
 
 
297
@dblocked
 
298
def dbupgrade(mainconfig=None):
 
299
 
 
300
    """ upgrade the database. """
 
301
 
 
302
    #time.sleep(10)
 
303
    print 'upgrading users'
 
304
    users = Session.query(UserHost).all()
 
305
    upgraded = []
 
306
 
 
307
    # populate the User table
 
308
    Session.begin()
 
309
    for user in users:
 
310
        name = user.name
 
311
        if name in upgraded:
 
312
            continue
 
313
        try:
 
314
            if not byname(name):
 
315
                newuser = User(name=name)
 
316
                session.add(newuser)
 
317
            upgraded.append(name)
 
318
        except sqlalchemy.exc.IntegrityError, ex:
 
319
            pass
 
320
        except:
 
321
            handle_exception()
 
322
 
 
323
    Session.commit()
 
324
    Session.close()
 
325
    print "upgraded: %s" % ' .. '.join(upgraded) 
 
326
    print 'upgrading email table'
 
327
    from gozerbot.database.db import Db
 
328
 
 
329
    # upgrade email table
 
330
    try:
 
331
        db = Db(config=mainconfig)
 
332
        if db.dbtype == 'mysql':
 
333
            db.execute("ALTER TABLE email ADD COLUMN email.order INT")
 
334
        else:
 
335
            db.execute("ALTER TABLE email ADD COLUMN 'order' INT")
 
336
    except Exception, ex:
 
337
        if 'already exists' in str(ex) or 'duplicate column name' in \
 
338
str(ex).lower():
 
339
            pass
 
340
        else:
 
341
            handle_exception()