changed the sfa db schema. All records are now stored in 1 table instead of createing...
[sfa.git] / sfa / util / genitable.py
1 # genitable.py
2 #
3 # implements support for geni records stored in db tables
4 #
5 # TODO: Use existing PLC database methods? or keep this separate?
6
7 ### $Id$
8 ### $URL$
9
10 import report
11 import  pgdb
12 from pg import DB, ProgrammingError
13 from sfa.trust.gid import *
14 from sfa.util.record import *
15 from sfa.util.debug import *
16 from sfa.util.config import *
17 from sfa.util.filter import *
18
19 class GeniTable(list):
20
21     GENI_TABLE_PREFIX = "sfa"
22
23     def __init__(self, record_filter = None):
24
25         # pgsql doesn't like table names with "." in them, to replace it with "$"
26         self.tablename = GeniTable.GENI_TABLE_PREFIX
27
28         # establish a connection to the pgsql server
29         cninfo = Config().get_plc_dbinfo()     
30         self.cnx = DB(cninfo['dbname'], cninfo['address'], port=cninfo['port'], user=cninfo['user'], passwd=cninfo['password'])
31
32         if record_filter:
33             records = self.find(record_filter)
34             for record in reocrds:
35                 self.append(record)             
36
37     def exists(self):
38         tableList = self.cnx.get_tables()
39         if 'public.' + self.tablename in tableList:
40             return True
41         if 'public."' + self.tablename + '"' in tableList:
42             return True
43         return False
44
45     def create(self):
46         
47         querystr = "CREATE TABLE " + self.tablename + " ( \
48                 record_id serial PRIMARY KEY , \
49                 hrn text NOT NULL, \
50                 authority text NOT NULL, \
51                 gid text, \
52                 type text NOT NULL, \
53                 pointer integer, \
54                 date_created timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, \
55                 last_updated timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP);"
56         template = "CREATE INDEX %s_%s_idx ON %s (%s);"
57         indexes = [template % ( self.tablename, field, self.tablename, field) \
58                    for field in ['hrn', 'type', 'authority', 'pointer']]
59         # IF EXISTS doenst exist in postgres < 8.2
60         try:
61             self.cnx.query('DROP TABLE IF EXISTS ' + self.tablename)
62         except ProgrammingError:
63             try:
64                 self.cnx.query('DROP TABLE ' + self.tablename)
65             except ProgrammingError:
66                 pass
67          
68         self.cnx.query(querystr)
69         for index in indexes:
70             self.cnx.query(index)
71
72     def remove(self, record):
73         query_str = "DELETE FROM %s WHERE record_id = %s" % (self.tablename, record['record_id']) 
74         self.cnx.query(query_str)
75
76     def insert(self, record):
77         dont_insert = ['date_created', 'last_updated']
78         fields = [field for field in  record.fields.keys() if field not in dont_insert]  
79         fieldnames = ["pointer"] + fields
80         fieldvals = record.get_field_value_strings(fieldnames)
81         query_str = "INSERT INTO " + self.tablename + \
82                        "(" + ",".join(fieldnames) + ") " + \
83                        "VALUES(" + ",".join(fieldvals) + ")"
84         #print query_str
85         self.cnx.query(query_str)
86
87     def update(self, record):
88         dont_update = ['date_created', 'last_updated']
89         fields = [field for field in  record.fields.keys() if field not in dont_update]  
90         fieldvals = record.get_field_value_strings(fields)
91         pairs = []
92         for field in fields:
93             val = record.get_field_value_string(field)
94             pairs.append(field + " = " + val)
95         update = ", ".join(pairs)
96
97         query_str = "UPDATE %s SET %s WHERE record_id = %s" % \
98                     (self.tablename, update, record['record_id'])
99         #print query_str
100         self.cnx.query(query_str)
101
102     def quote(self, value):
103         """
104         Returns quoted version of the specified value.
105         """
106
107         # The pgdb._quote function is good enough for general SQL
108         # quoting, except for array types.
109         if isinstance(value, (list, tuple, set)):
110             return "ARRAY[%s]" % ", ".join(map, self.quote, value)
111         else:
112             return pgdb._quote(value)
113
114     def find(self, record_filter = None):
115         sql = "SELECT * FROM %s WHERE True " % self.tablename
116         
117         if isinstance(record_filter, (list, tuple, set)):
118             ints = filter(lambda x: isinstance(x, (int, long)), record_filter)
119             strs = filter(lambda x: isinstance(x, StringTypes), record_filter)
120             record_filter = Filter(GeniRecord.all_fields, {'record_id': ints, 'hrn': strs})
121             sql += "AND (%s) %s " % record_filter.sql("OR") 
122         elif isinstance(record_filter, dict):
123             record_filter = Filter(GeniRecord.all_fields, record_filter)        
124             sql += " AND (%s) %s" % record_filter.sql("AND")
125         elif isinstance(record_filter, StringTypes):
126             record_filter = Filter(GeniRecord.all_fields, {'hrn':[record_filter]})    
127             sql += " AND (%s) %s" % record_filter.sql("AND")
128         elif isinstance(record_filter, int):
129             record_filter = Filter(GeniRecord.all_fields, {'record_id':[record_filter]})    
130             sql += " AND (%s) %s" % record_filter.sql("AND")
131         results = self.cnx.query(sql).dictresult()
132         return results
133
134     def findObjects(self, record_filter = None):
135         
136         results = self.find(record_filter) 
137         result_rec_list = []
138         for result in results:
139             if result['type'] in ['authority']:
140                 result_rec_list.append(AuthorityRecord(dict=result))
141             elif result['type'] in ['node']:
142                 result_rec_list.append(NodeRecord(dict=result))
143             elif result['type'] in ['slice']:
144                 result_rec_list.append(SliceRecord(dict=result))
145             elif result['type'] in ['user']:
146                 result_rec_list.append(UserRecord(dict=result))
147             else:
148                 result_rec_list.append(GeniRecord(dict=result))
149         return result_rec_list
150
151
152     def drop(self):
153         try:
154             self.cnx.query('DROP TABLE IF EXISTS ' + self.tablename)
155         except ProgrammingError:
156             try:
157                 self.cnx.query('DROP TABLE ' + self.tablename)
158             except ProgrammingError:
159                 pass
160     
161     @staticmethod
162     def geni_records_purge(cninfo):
163
164         cnx = DB(cninfo['dbname'], cninfo['address'], 
165                  port=cninfo['port'], user=cninfo['user'], passwd=cninfo['password'])
166         tableList = cnx.get_tables()
167         for table in tableList:
168             if table.startswith(GeniTable.GENI_TABLE_PREFIX) or \
169                     table.startswith('public.' + GeniTable.GENI_TABLE_PREFIX) or \
170                     table.startswith('public."' + GeniTable.GENI_TABLE_PREFIX):
171                 report.trace("dropping table " + table)
172                 cnx.query("DROP TABLE " + table)