- added 'enabled' to Site.fields
[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.Addresses import Address, Addresses
13 from PLC.Persons import Person, Persons
14
15 class Site(Row):
16     """
17     Representation of a row in the sites table. To use, optionally
18     instantiate with a dict of values. Update as you would a
19     dict. Commit to the database with sync().
20     """
21
22     table_name = 'sites'
23     primary_key = 'site_id'
24     join_tables = ['person_site', 'site_address', 'peer_site']
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         'enabled': Parameter(bool, "Has been enabled"),
32         'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
33         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
34         'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
35         'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
36         'last_updated': Parameter(int, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True),
37         'max_slices': Parameter(int, "Maximum number of slices that the site is able to create"),
38         'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create"),
39         'person_ids': Parameter([int], "List of account identifiers"),
40         'slice_ids': Parameter([int], "List of slice identifiers"),
41         'address_ids': Parameter([int], "List of address identifiers"),
42         'pcu_ids': Parameter([int], "List of PCU identifiers"),
43         'node_ids': Parameter([int], "List of site node identifiers"),
44         'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
45         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
46         }
47
48     # for Cache
49     class_key = 'login_base'
50     foreign_fields = ['abbreviated_name', 'name', 'is_public', 'latitude', 'longitude',
51                       'url', 'max_slices', 'max_slivers',
52                       ]
53     # forget about these ones, they are read-only anyway
54     # handling them causes Cache to re-sync all over again 
55     # 'last_updated', 'date_created'
56     foreign_xrefs = []
57
58     def validate_name(self, name):
59         if not len(name):
60             raise PLCInvalidArgument, "Name must be specified"
61
62         return name
63
64     validate_abbreviated_name = validate_name
65
66     def validate_login_base(self, login_base):
67         if not len(login_base):
68             raise PLCInvalidArgument, "Login base must be specified"
69
70         if not set(login_base).issubset(string.ascii_letters.lower()):
71             raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters"
72
73         conflicts = Sites(self.api, [login_base])
74         for site in conflicts:
75             if 'site_id' not in self or self['site_id'] != site['site_id']:
76                 raise PLCInvalidArgument, "login_base already in use"
77
78         return login_base
79
80     def validate_latitude(self, latitude):
81         if not self.has_key('longitude') or \
82            self['longitude'] is None:
83             raise PLCInvalidArgument, "Longitude must also be specified"
84
85         return latitude
86
87     def validate_longitude(self, longitude):
88         if not self.has_key('latitude') or \
89            self['latitude'] is None:
90             raise PLCInvalidArgument, "Latitude must also be specified"
91
92         return longitude
93
94     validate_date_created = Row.validate_timestamp
95     validate_last_updated = Row.validate_timestamp
96
97     add_person = Row.add_object(Person, 'person_site')
98     remove_person = Row.remove_object(Person, 'person_site')
99
100     add_address = Row.add_object(Address, 'site_address')
101     remove_address = Row.remove_object(Address, 'site_address')
102
103     def delete(self, commit = True):
104         """
105         Delete existing site.
106         """
107
108         assert 'site_id' in self
109
110         # Delete accounts of all people at the site who are not
111         # members of at least one other non-deleted site.
112         persons = Persons(self.api, self['person_ids'])
113         for person in persons:
114             delete = True
115
116             person_sites = Sites(self.api, person['site_ids'])
117             for person_site in person_sites:
118                 if person_site['site_id'] != self['site_id']:
119                     delete = False
120                     break
121
122             if delete:
123                 person.delete(commit = False)
124
125         # Delete all site addresses
126         addresses = Addresses(self.api, self['address_ids'])
127         for address in addresses:
128             address.delete(commit = False)
129
130         # Delete all site slices
131         slices = Slices(self.api, self['slice_ids'])
132         for slice in slices:
133             slice.delete(commit = False)
134
135         # Delete all site PCUs
136         pcus = PCUs(self.api, self['pcu_ids'])
137         for pcu in pcus:
138             pcu.delete(commit = False)
139
140         # Delete all site nodes
141         nodes = Nodes(self.api, self['node_ids'])
142         for node in nodes:
143             node.delete(commit = False)
144
145         # Clean up miscellaneous join tables
146         for table in self.join_tables:
147             self.api.db.do("DELETE FROM %s WHERE site_id = %d" % \
148                            (table, self['site_id']))
149
150         # Mark as deleted
151         self['deleted'] = True
152         self.sync(commit)
153
154 class Sites(Table):
155     """
156     Representation of row(s) from the sites table in the
157     database.
158     """
159
160     def __init__(self, api, site_filter = None, columns = None):
161         Table.__init__(self, api, Site, columns)
162
163         sql = "SELECT %s FROM view_sites WHERE deleted IS False" % \
164               ", ".join(self.columns)
165
166         if site_filter is not None:
167             if isinstance(site_filter, (list, tuple, set)):
168                 # Separate the list into integers and strings
169                 ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
170                 strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
171                 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
172                 sql += " AND (%s)" % site_filter.sql(api, "OR")
173             elif isinstance(site_filter, dict):
174                 site_filter = Filter(Site.fields, site_filter)
175                 sql += " AND (%s)" % site_filter.sql(api, "AND")
176
177         self.selectall(sql)