create() calls get_table()
[plcapi.git] / PLC / Slices.py
1 from types import StringTypes
2 import time
3 import re
4 import datetime
5
6 from PLC.Faults import *
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Debug import profile
9 from PLC.Nodes import Node
10 from PLC.Persons import Person, Persons
11 from PLC.SlicePersons import SlicePerson, SlicePersons
12 from PLC.SliceNodes import SliceNode, SliceNodes
13 from PLC.SliceTags import SliceTag, SliceTags
14 from PLC.Timestamp import Timestamp
15 from PLC.Storage.AlchemyObject import AlchemyObj
16
17 class Slice(AlchemyObj):
18     """
19     Representation of a row in the slices table. To use, optionally
20     instantiate with a dict of values. Update as you would a
21     dict. Commit to the database with sync().To use, instantiate
22     with a dict of values.
23     """
24
25     tablename = 'slices'
26  
27     fields = {
28         'slice_id': Parameter(int, "Slice identifier", primary_key=True),
29         'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
30         'tenant_id': Parameter(int, "Keystone tenant identifier"), 
31         'name': Parameter(str, "Slice name", max = 32),
32         'instantiation': Parameter(str, "Slice instantiation state", nullok=True),
33         'url': Parameter(str, "URL further describing this slice", max = 254, nullok = True),
34         'description': Parameter(str, "Slice description", max = 2048, nullok = True),
35         'max_nodes': Parameter(int, "Maximum number of nodes that can be assigned to this slice"),
36         'creator_person_id': Parameter(str, "Identifier of the account that created this slice"),
37         'created': Parameter(datetime, "Date and time when slice was created, in seconds since UNIX epoch", ro = True),
38         'expires': Parameter(datetime, "Date and time when slice expires, in seconds since UNIX epoch"),
39         'node_ids': Parameter([str], "List of nodes in this slice", joined = True),
40         'person_ids': Parameter([str], "List of accounts that can use this slice", joined = True),
41         'slice_tag_ids': Parameter([int], "List of slice attributes", joined = True),
42         'peer_id': Parameter(int, "Peer to which this slice belongs", nullok = True),
43         'peer_slice_id': Parameter(int, "Foreign slice identifier at peer", nullok = True),
44         }
45     tags = {}
46
47     def validate_name(self, name):
48         # N.B.: Responsibility of the caller to ensure that login_base
49         # portion of the slice name corresponds to a valid site, if
50         # desired.
51
52         # 1. Lowercase.
53         # 2. Begins with login_base (letters or numbers).
54         # 3. Then single underscore after login_base.
55         # 4. Then letters, numbers, or underscores.
56         good_name = r'^[a-z0-9]+_[a-zA-Z0-9_]+$'
57         if not name or \
58            not re.match(good_name, name):
59             raise PLCInvalidArgument, "Invalid slice name"
60
61         conflicts = Slices(self.api, [name])
62         for slice in conflicts:
63             if 'slice_id' not in self or self['slice_id'] != slice.slice_id:
64                 raise PLCInvalidArgument, "Slice name already in use, %s"%name
65
66         return name
67
68     def validate_expires(self, expires):
69         # N.B.: Responsibility of the caller to ensure that expires is
70         # not too far into the future.
71         check_future = not ('is_deleted' in self and self['is_deleted'])
72         return Timestamp.sql_validate( expires, check_future = check_future)
73
74     #add_person = Row.add_object(Person, 'slice_person')
75     #remove_person = Row.remove_object(Person, 'slice_person')
76
77     #add_node = Row.add_object(Node, 'slice_node')
78     #remove_node = Row.remove_object(Node, 'slice_node')
79
80     #add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
81     #delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
82
83     def sync(self, commit = True, validate=True):
84         """
85         Add or update a slice.
86         """
87         # sync the nova record and the plc record
88         AlchemyObj.sync(self, commit=commit, validate=validate)
89         # create the nova record
90         nova_fields = ['enabled', 'name', 'description']
91         nova_can_update = lambda (field, value): field in nova_fields
92         nova_slice = dict(filter(nova_can_update, self.items()))
93         if 'slice_id' not in self:
94             # Before a new slice is added, delete expired slices
95             #expired = Slices(self.api, expires = -int(time.time()))
96             #for slice in expired:
97             #    slice.delete(commit)
98             self.object = self.api.client_shell.keystone.tenants.create(**nova_slice)
99             self['tenant_id'] = self.object.id
100             AlchemyObj.insert(self, dict(self))
101         else:
102             self.object = self.api.client_shell.keystone.tenants.update(self['tenant_id'], **nova_slice) 
103             AlchemyObj.update(self, {'slice_id': self['slice_id']}, dict(self)) 
104
105     def delete(self, commit = True):
106         """
107         Delete existing slice.
108         """
109         assert 'slice_id' in self
110         assert 'tenant_id' in self
111
112         # delete the nova object
113         tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
114         self.api.client_shell.keystone.tenants.delete(tenant)
115
116         # delete relationships
117         SlicePerson().delete(self, filter={'slice_id': self['slice_id']}) 
118         SliceNode().delete(self, filter={'slice_id': self['slice_id']}) 
119         SliceTag().delete(self, filter={'slice_id': self['slice_id']})
120         
121         # delete slice 
122         AlchemyObj.delete(self, dict(self))
123
124
125 class Slices(list):
126     """
127     Representation of row(s) from the slices table in the
128     database.
129     """
130
131     def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
132          
133         # the view that we're selecting upon: start with view_slices
134         if not slice_filter:
135             slices = Slice().select()
136         elif isinstance (slice_filter, StringTypes):
137             slices = Slice().select(filter={'name': slice_filter})
138         elif isinstance(slice_filter, dict):
139             slices = Slice().select(filter=slice_filter)
140         elif isinstance(slice_filter, (list, tuple, set)):
141             slices = Slice().select()
142             slices = [slice for slice in slices if slice.id in slice_filter]
143         else:
144             raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter
145
146         for slice in slices:
147             slice = Slice(api, object=slice)
148             if not columns or 'person_ids' in columns:
149                 slice_persons = SlicePerson().select(filter={'slice_id': slice.id})
150                 slice['person_ids'] = [rec.person_id for rec in slice_persons] 
151                 
152             if not columns or 'node_ids' in columns:
153                 slice_nodes = SliceNode().select(filter={'slice_id': slice.id})
154                 slice['node_ids'] = [rec.node_id for rec in slice_nodes]
155
156             if not columns or 'slice_tag_ids' in columns:
157                 slice_tags = SliceTag().select(filter={'slice_id': slice.id})
158                 slice['slice_tag_ids'] = [rec.slice_tag_id for rec in slice_tags]
159                 
160             self.append(slice)