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