fix parameter types. added validate_last_updated(). added add_person(), delete_person
[plcapi.git] / PLC / Sites.py
1 from types import StringTypes
2 from datetime import datetime
3 import string
4
5 from PLC.Faults import *
6 from PLC.Logger import logger
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Storage.AlchemyObject import AlchemyObj
9 from PLC.Slices import Slice, Slices
10 from PLC.Persons import Person, Persons
11 from PLC.SitePersons import SitePerson, SitePersons
12 from PLC.Addresses import Address, Addresses
13 from PLC.PCUs import PCU, PCUs
14 from PLC.Nodes import Node, Nodes
15 from PLC.SiteTags import SiteTag, SiteTags
16
17 class Site(AlchemyObj):
18     """
19     Representation of a row in the sites table. To use, optionally
20     instantiate with a dict of values. Update as you would a
21     dict. Commit to the database with sync().
22     """
23
24     tablename = 'sites'
25
26     fields = {
27         'site_id': Parameter(int, "Site identifier", primary_key=True),
28         'tenant_id': Parameter(str, "Tenant identifier"),
29         'enabled': Parameter(bool, "Has been enabled"),
30         'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
31         'login_base': Parameter(str, "Site slice prefix", max = 20),
32         'is_public': Parameter(bool, "Publicly viewable site"),
33         'name': Parameter(str, "Full site name", max = 254),
34         'description': Parameter(str, "Description", max = 254),
35         'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
36         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
37         'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
38         'date_created': Parameter(datetime, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True, default=datetime.now()), 
39         'last_updated': Parameter(datetime, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True, nullok=True), 
40         'max_slices': Parameter(int, "Maximum number of slices that the site is able to create", default=10),
41         'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create", default=1000),
42         'person_ids': Parameter([int], "List of account identifiers", joined=True),
43         'slice_ids': Parameter([int], "List of slice identifiers", joined=True),
44         'address_ids': Parameter([int], "List of address identifiers", joined=True),
45         'pcu_ids': Parameter([int], "List of PCU identifiers", joined=True),
46         'node_ids': Parameter([int], "List of site node identifiers", joined=True),
47         'site_tag_ids' : Parameter ([int], "List of tags attached to this site", joined=True),
48         'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
49         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
50         'ext_consortium_id': Parameter(int, "external consortium id", nullok = True) 
51         }
52
53     def validate_last_updated(self, last_updated):
54         # always return current timestamp 
55         last_updated = datetime.now()
56         return last_updated
57
58     def add_person(self, person_filter, role=None):
59         assert 'site_id' in self
60         assert 'tenant_id' in self
61         if not role:
62             role = 'user'
63         tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id']) 
64         persons = Persons(self.api, person_filter)
65         for person in persons:
66             keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
67             tenant.add_user(keystone_user, role)  
68             site_person = SitePerson(self.api, {'site_id': self['id'], 
69                                                 'person_id': person['person_id']}) 
70             site_person.sync()
71
72
73     def delete_person(self, person_filter, role=None):
74         assert 'site_id' in self
75         assert 'tenant_id' in self
76         if not role:
77             role = 'user'
78         tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
79         persons = Persons(self.api, person_filter)
80         for person in persons:
81             keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
82             tenant.remove_user(keystone_user, role)
83             site_persons = SitePersons(self.api, {'site_id': self['id'],
84                                                 'person_id': person['person_id']})
85             for site_person in site_persons:
86                 site_person.delete()    
87           
88
89     def sync(self, commit=True, validate=True):
90         """
91         Add or update the site.
92         """
93         assert 'login_base' in self
94         # sync the nova record and the plc record
95         AlchemyObj.sync(self, commit=commit, validate=validate)     
96         nova_fields = ['enabled', 'description']
97         nova_can_update = lambda (field, value): field in nova_fields
98         nova_site = dict(filter(nova_can_update, self.items()))
99         nova_site['tenant_name'] = self['login_base']
100         if 'site_id' not in self:
101             tenants = self.api.client_shell.keystone.tenants.findall(name=self['login_base'])
102             if not tenants:
103                 self.object = self.api.client_shell.keystone.tenants.create(**nova_site)
104             else:
105                 self.object = tenants[0]
106             self['tenant_id'] = self.object.id
107             AlchemyObj.insert(self, dict(self)) 
108         else:
109             self.object = self.api.client_shell.keystone.tenants.update(self['tenant_id'], **nova_site)
110             AlchemyObj.update(self, {'site_id': self['site_id']}, dict(self))
111
112     def delete(self, commit=True):
113         assert 'site_id' in self
114         assert 'tenant_id' in self
115         
116         # delete nova object
117         tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
118         self.api.client_shell.keystone.tenants.delete(tenant)
119         
120         # delete relationships
121         SitePerson().delete(filter={'site_id': self['site_id']}) 
122         Slice().delete(filter={'site_id': self['site_id']}) 
123         PCU().delete(filter={'site_id': self['site_id']}) 
124         Node().delete(filter={'site_id': self['site_id']}) 
125         Address().delete(filter={'site_id': self['site_id']}) 
126         SiteTag().delete(filter={'site_id': self['site_id']}) 
127         
128         # delete site
129         AlchemyObj.delete(self, dict(self))        
130         
131                
132
133 class Sites(list):
134     """
135     Representation of row(s) from the sites table in the
136     database.
137     """
138
139     def __init__(self, api, site_filter = None, columns = None):
140         self.api = api 
141         if not site_filter:
142             sites = Site().select() 
143         elif isinstance(site_filter, int):
144             sites = Site().select(filter={'site_id': site_filter})
145         elif isinstance(site_filter, StringTypes):
146             sites = Site().select(filter={'login_base': site_filter})
147         elif isinstance(site_filter, dict):
148             sites = Site().select(filter=site_filter)
149         elif isinstance(site_filter, (list, tuple, set)):
150             sites = Site().select() 
151             sites = [site for site in sites if site.id in site_filter]
152         else:
153             raise PLCInvalidArgument, "Wrong site filter %s" % site_filter         
154
155         for site in sites:
156             site = Site(self.api, object = site)
157             if not columns or 'person_ids' in columns:
158                 site_persons = SitePerson().select(filter={'site_id': site.id})
159                 site['person_ids'] = [rec.person_id for rec in site_persons]
160
161             if not columns or 'slice_ids' in columns:
162                 site_slices = Slice().select(filter={'site_id': site.id})
163                 site['slice_ids'] = [rec.person_id for rec in site_slices]
164
165             if not columns or 'puc_ids' in columns:
166                 site_pcus = PCU().select(filter={'site_id': site.id})
167                 site['pcu_ids'] = [rec.id for rec in site_pcus]
168
169             if not columns or 'node_ids' in columns:
170                 site_nodes = Node().select(filter={'site_id': site.id})
171                 site['node_ids'] = [rec.id for rec in site_nodes]
172
173             if not columns or 'address_ids' in columns:
174                 site_addresses = Address().select(filter={'site_id': site.id})
175                 site['address_ids'] = [rec.id for rec in site_addresses]
176
177             if not columns or 'site_tag_ids' in columns:
178                 site_tags = SiteTag().select(filter={'site_id': site.id})
179                 site['site_tag_ids'] = [rec.id for rec in site_tags]
180
181             self.append(site)