updated fields
[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.Filter import Filter
9 from PLC.Debug import profile
10 from PLC.Nodes import Node
11 from PLC.Persons import Person, Persons
12 from PLC.SlicePersons import SlicePerson, SlicePersons
13 from PLC.SliceNodes import SliceNode, SliceNodes
14 from PLC.SliceTags import SliceTag, SliceTags
15 from PLC.Timestamp import Timestamp
16 from PLC.Storage.AlchemyObject import AlchemyObj
17
18 class Slice(AlchemyObj):
19     """
20     Representation of a row in the slices table. To use, optionally
21     instantiate with a dict of values. Update as you would a
22     dict. Commit to the database with sync().To use, instantiate
23     with a dict of values.
24     """
25
26     tablename = 'slices'
27  
28     fields = {
29         'slice_id': Parameter(int, "Slice identifier", primary_key=True),
30         'site_id': Parameter(int, "Identifier of the site to which this slice belongs"),
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         AlchemyObj.sync(self, commit, validate)
88         if 'slice_id' not in self:
89             # Before a new slice is added, delete expired slices
90             #expired = Slices(self.api, expires = -int(time.time()))
91             #for slice in expired:
92             #    slice.delete(commit)
93             AlchemyObj.insert(self, dict(self))
94         else:
95             AlchemyObj.update(self, self['slice_id'], dict(self)) 
96
97     def delete(self, commit = True):
98         """
99         Delete existing slice.
100         """
101         assert 'slice_id' in self
102         # delete relationships
103         SlicePerson().delete(self, filter={'slice_id': self['slice_id']}) 
104         SliceNode().delete(self, filter={'slice_id': self['slice_id']}) 
105         SliceTag().delete(self, filter={'slice_id': self['slice_id']}) 
106         AlchemyObj.delete(self, dict(self))
107
108
109 class Slices(list):
110     """
111     Representation of row(s) from the slices table in the
112     database.
113     """
114
115     def __init__(self, api, slice_filter = None, columns = None, expires = int(time.time())):
116          
117         # the view that we're selecting upon: start with view_slices
118         if not slice_filter:
119             slices = Slice().select()
120         elif isinstance (slice_filter, StringTypes):
121             slices = Slice().select(filter={'name': slice_filter})
122         elif isinstance(slice_filter, dict):
123             slices = Slice().select(filter=slice_filter)
124         elif isinstance(slice_filter, (list, tuple, set)):
125             slices = Slice().select()
126             slices = [slice for slice in slices if slice.id in slice_filter]
127         else:
128             raise PLCInvalidArgument, "Wrong slice filter %r"%slice_filter
129
130         for slice in slices:
131             slice = Slice(api, object=slice)
132             if not columns or 'person_ids' in columns:
133                 slice_persons = SlicePerson().select(filter={'slice_id': slice.id})
134                 slice['person_ids'] = [rec.person_id for rec in slice_persons] 
135                 
136             if not columns or 'node_ids' in columns:
137                 slice_nodes = SliceNode().select(filter={'slice_id': slice.id})
138                 slice['node_ids'] = [rec.node_id for rec in slice_nodes]
139
140             if not columns or 'slice_tag_ids' in columns:
141                 slice_tags = SliceTag().select(filter={'slice_id': slice.id})
142                 slice['slice_tag_ids'] = [rec.slice_tag_id for rec in slice_tags]
143                 
144             self.append(slice)