- use Filter to select rows
[plcapi.git] / PLC / Sites.py
1 from types import StringTypes
2 import string
3
4 from PLC.Faults import *
5 from PLC.Parameter import Parameter
6 from PLC.Filter import Filter
7 from PLC.Debug import profile
8 from PLC.Table import Row, Table
9 from PLC.Slices import Slice, Slices
10 from PLC.PCUs import PCU, PCUs
11 from PLC.Nodes import Node, Nodes
12 from PLC.NodeGroups import NodeGroup, NodeGroups
13 from PLC.Addresses import Address, Addresses
14 import PLC.Persons
15
16 class Site(Row):
17     """
18     Representation of a row in the sites table. To use, optionally
19     instantiate with a dict of values. Update as you would a
20     dict. Commit to the database with sync().
21     """
22
23     table_name = 'sites'
24     primary_key = 'site_id'
25     fields = {
26         'site_id': Parameter(int, "Site identifier"),
27         'name': Parameter(str, "Full site name", max = 254),
28         'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
29         'login_base': Parameter(str, "Site slice prefix", max = 20),
30         'is_public': Parameter(bool, "Publicly viewable site"),
31         'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
32         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
33         'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
34         'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
35         'last_updated': Parameter(int, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True),
36         'max_slices': Parameter(int, "Maximum number of slices that the site is able to create"),
37         'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create"),
38         'person_ids': Parameter([int], "List of account identifiers"),
39         'slice_ids': Parameter([int], "List of slice identifiers"),
40         'address_ids': Parameter([int], "List of address identifiers"),
41         'pcu_ids': Parameter([int], "List of PCU identifiers"),
42         'node_ids': Parameter([int], "List of site node identifiers"),
43         }
44
45     def validate_name(self, name):
46         if not len(name):
47             raise PLCInvalidArgument, "Name must be specified"
48
49         return name
50
51     validate_abbreviated_name = validate_name
52
53     def validate_login_base(self, login_base):
54         if not len(login_base):
55             raise PLCInvalidArgument, "Login base must be specified"
56
57         if not set(login_base).issubset(string.ascii_letters.lower()):
58             raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters"
59
60         conflicts = Sites(self.api, [login_base])
61         for site_id, site in conflicts.iteritems():
62             if 'site_id' not in self or self['site_id'] != site_id:
63                 raise PLCInvalidArgument, "login_base already in use"
64
65         return login_base
66
67     def validate_latitude(self, latitude):
68         if not self.has_key('longitude') or \
69            self['longitude'] is None:
70             raise PLCInvalidArgument, "Longitude must also be specified"
71
72         return latitude
73
74     def validate_longitude(self, longitude):
75         if not self.has_key('latitude') or \
76            self['latitude'] is None:
77             raise PLCInvalidArgument, "Latitude must also be specified"
78
79         return longitude
80
81     def add_person(self, person, commit = True):
82         """
83         Add person to existing site.
84         """
85
86         assert 'site_id' in self
87         assert isinstance(person, PLC.Persons.Person)
88         assert 'person_id' in person
89
90         site_id = self['site_id']
91         person_id = person['person_id']
92
93         if person_id not in self['person_ids']:
94             assert site_id not in person['site_ids']
95
96             self.api.db.do("INSERT INTO person_site (person_id, site_id)" \
97                            " VALUES(%(person_id)d, %(site_id)d)",
98                            locals())
99
100             if commit:
101                 self.api.db.commit()
102
103             self['person_ids'].append(person_id)
104             person['site_ids'].append(site_id)
105
106     def remove_person(self, person, commit = True):
107         """
108         Remove person from existing site.
109         """
110
111         assert 'site_id' in self
112         assert isinstance(person, PLC.Persons.Person)
113         assert 'person_id' in person
114
115         site_id = self['site_id']
116         person_id = person['person_id']
117
118         if person_id in self['person_ids']:
119             assert site_id in person['site_ids']
120
121             self.api.db.do("DELETE FROM person_site" \
122                            " WHERE person_id = %(person_id)d" \
123                            " AND site_id = %(site_id)d",
124                            locals())
125
126             if commit:
127                 self.api.db.commit()
128
129             self['person_ids'].remove(person_id)
130             person['site_ids'].remove(site_id)
131
132     def add_address(self, address, commit = True):
133         """
134         Add address to existing site.
135         """
136
137         assert 'site_id' in self
138         assert isinstance(address, Address)
139         assert 'address_id' in address
140
141         site_id = self['site_id']
142         address_id = address['address_id']
143
144         if address_id not in self['address_ids']:
145             self.api.db.do("INSERT INTO site_address (address_id, site_id)" \
146                            " VALUES(%(address_id)d, %(site_id)d)",
147                            locals())
148
149             if commit:
150                 self.api.db.commit()
151
152             self['address_ids'].append(address_id)
153
154     def remove_address(self, address, commit = True):
155         """
156         Remove address from existing site.
157         """
158
159         assert 'site_id' in self
160         assert isinstance(address, Address)
161         assert 'address_id' in address
162
163         site_id = self['site_id']
164         address_id = address['address_id']
165
166         if address_id in self['address_ids']:
167             self.api.db.do("DELETE FROM site_address" \
168                            " WHERE address_id = %(address_id)d" \
169                            " AND site_id = %(site_id)d",
170                            locals())
171
172             if commit:
173                 self.api.db.commit()
174
175             self['address_ids'].remove(address_id)
176
177     def delete(self, commit = True):
178         """
179         Delete existing site.
180         """
181
182         assert 'site_id' in self
183
184         # Delete accounts of all people at the site who are not
185         # members of at least one other non-deleted site.
186         persons = PLC.Persons.Persons(self.api, self['person_ids'])
187         for person_id, person in persons.iteritems():
188             delete = True
189
190             person_sites = Sites(self.api, person['site_ids'])
191             for person_site_id, person_site in person_sites.iteritems():
192                 if person_site_id != self['site_id']:
193                     delete = False
194                     break
195
196             if delete:
197                 person.delete(commit = False)
198
199         # Delete all site addresses
200         addresses = Addresses(self.api, self['address_ids'])
201         for address in addresses.values():
202            address.delete(commit = False)
203
204         # Delete all site slices
205         slices = Slices(self.api, self['slice_ids'])
206         for slice in slices.values():
207            slice.delete(commit = False)
208
209         # Delete all site PCUs
210         pcus = PCUs(self.api, self['pcu_ids'])
211         for pcu in pcus.values():
212            pcu.delete(commit = False)
213
214         # Delete all site nodes
215         nodes = Nodes(self.api, self['node_ids'])
216         for node in nodes.values():
217             node.delete(commit = False)
218
219         # Clean up miscellaneous join tables
220         for table in ['person_site']:
221             self.api.db.do("DELETE FROM %s" \
222                            " WHERE site_id = %d" % \
223                            (table, self['site_id']), self)
224
225         # Mark as deleted
226         self['deleted'] = True
227         self.sync(commit)
228
229 class Sites(Table):
230     """
231     Representation of row(s) from the sites table in the
232     database.
233     """
234
235     def __init__(self, api, site_filter = None):
236         Table.__init__(self, api, Site)
237
238         sql = "SELECT %s FROM view_sites WHERE deleted IS False" % \
239               ", ".join(Site.fields)
240
241         if site_filter is not None:
242             if isinstance(site_filter, list):
243                 # Separate the list into integers and strings
244                 ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
245                 strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
246                 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
247                 sql += " AND (%s)" % site_filter.sql(api, "OR")
248             elif isinstance(site_filter, dict):
249                 site_filter = Filter(Site.fields, site_filter)
250                 sql += " AND (%s)" % site_filter.sql(api, "AND")
251
252         self.selectall(sql)