added create_network(), delete_network(), create_subnet(), delete_subnet(), process_t...
[plcapi.git] / PLC / Peers.py
1 #
2 # Functions for interacting with the persons peers in the database
3 #
4
5 #
6 #
7
8 import re
9 from types import StringTypes
10 import traceback
11 from urlparse import urlparse
12
13 import PLC.Auth
14 from PLC.Debug import log
15 from PLC.Faults import *
16 from PLC.Namespace import hostname_to_hrn
17 from PLC.Parameter import Parameter, Mixed
18 from PLC.Filter import Filter
19 from PLC.Table import Row, Table
20 from PLC.Sites import Site, Sites
21 from PLC.Persons import Person, Persons
22 from PLC.Keys import Key, Keys
23 from PLC.Nodes import Node, Nodes
24 from PLC.TagTypes import TagType, TagTypes
25 from PLC.NodeTags import NodeTag, NodeTags
26 from PLC.SliceTags import SliceTag, SliceTags
27 from PLC.Slices import Slice, Slices
28 from PLC.Storage.AlchemyObject import AlchemyObj
29
30 class Peer(AlchemyObj):
31     """
32     Representation of a row in the peers table. To use, optionally
33     instantiate with a dict of values. Update as you would a
34     dict. Commit to the database with sync().
35     """
36
37     tablename = 'peers'
38
39     fields = {
40         'peer_id': Parameter (int, "Peer identifier", primary_key=True),
41         'peername': Parameter (str, "Peer name"),
42         'peer_url': Parameter (str, "Peer API URL"),
43         'key': Parameter(str, "Peer GPG public key"),
44         'cacert': Parameter(str, "Peer SSL public certificate"),
45         'shortname' : Parameter(str, "Peer short name"),
46         'hrn_root' : Parameter(str, "Root of this peer in a hierarchical naming space"),
47         ### cross refs
48         'site_ids': Parameter([int], "List of sites for which this peer is authoritative", joined=True),
49         'person_ids': Parameter([int], "List of users for which this peer is authoritative", joined=True),
50         'key_ids': Parameter([int], "List of keys for which this peer is authoritative", joined=True),
51         'node_ids': Parameter([int], "List of nodes for which this peer is authoritative", joined=True),
52         'slice_ids': Parameter([int], "List of slices for which this peer is authoritative", joined=True),
53         }
54
55     def validate_peer_url(self, url):
56         """
57         Validate URL. Must be HTTPS.
58         """
59
60         (scheme, netloc, path, params, query, fragment) = urlparse(url)
61         if scheme != "https":
62             raise PLCInvalidArgument, "Peer URL scheme must be https"
63         if path[-1] != '/':
64             raise PLCInvalidArgument, "Peer URL should end with /"
65
66         return url
67
68     def validate_peername(self, peername):
69
70         if not len(peername):
71             raise PLCInvalidArgument, "Peer name must be specified"
72
73         conflicts = Peer().select(filter={'peername': peername})
74         for peer in conflicts:
75             if 'peer_id' not in self or self['peer_id'] != peer.peer_id:
76                 raise PLCInvalidArgument, "Peer name already in use"
77         return peername
78             
79
80     def add_site(self, site, peer_site_id, commit=True):
81         assert 'peer_id' in self
82         fields = {'peer_id': self['peer_id'],
83                      'site_id': site['site_id'],
84                      'peer_site_id': peer_site_id} 
85         peer_site = PeerSite(self.api, fields)
86         peer_site.sync()
87
88     def remove_site(self, site_filter, commit = True):
89         assert 'peer_id' in self
90         assert 'site_id' in site_filter
91         PeerSite().delete(self, site_filter)
92
93     def add_person(self, person, peer_person_id, commit = True):
94         assert 'peer_id' in self
95         fields = {'peer_id': self['peer_id'],
96                   'person_id': person['person_id'],
97                   'peer_person_id': peer_person_id} 
98         peer_person = PeerPerson(self.api, fields)       
99         peer_person.sync()
100
101     def remove_person(self, person_filter, commit = True):
102         assert 'peer_id' in self
103         assert 'person_id' in person_filter
104         PeerPerson().delete(self, person_filter)        
105
106     def add_key(self, key, peer_key_id, commit = True):
107         assert 'peer_id' in self
108         fields = {'peer_id': self['peer_id'],
109                   'key_id': key['key_id'],
110                   'peer_key_id': peer_key_id}
111         peer_key = PeerKey(self.api, fields)
112         peer_key.sync()
113
114     def remove_key(self, key_filter, commit = True):
115         assert 'peer_id' in self
116         assert 'key_id' in key_filter
117         PeerKey().delete(self, key_filter)
118
119
120     def add_node(self, node, peer_node_id, commit = True):
121         assert 'peer_id' in self
122         fields = {'peer_id': self['peer_id'],
123                   'node_id': node['node_id'],
124                   'peer_person_id': peer_node_id}
125         peer_node = PeerNode(self.api, fields)
126         peer_node.sync()
127
128     def remove_node(self, node_filter, commit = True):
129         assert 'peer_id' in self
130         assert 'node_id' in node_filter
131         PeerNode().delete(self, node_filter)
132
133     def add_slice(self, slice, peer_slice_id, commit = True):
134         assert 'peer_id' in self
135         fields = {'peer_id': self['peer_id'],
136                   'slice_id': slice['slice_id'],
137                   'peer_person_id': peer_person_id}
138         peer_slice = PeerSlice(self.api, fields)
139         peer_slice.sync()
140
141     def remove_slice(self, slice_filter, commit = True):
142         assert 'peer_id' in self
143         assert 'slice_id' in slice_filter
144         PeerSlice().delete(self, slice_filter)
145         
146     def sync(self, commit=True, validate=True):
147         AlchemyObj.sync(self, commit=commit, validate=validate)
148         # filter out fields that are not supported in keystone
149         if 'peer_id' not in self:
150             AlchemyObj.insert(self, dict(self))
151         else:
152             AlchemyObj.update(self, {'peer_id': self['peer_id']}, dict(self))
153         
154
155     def delete(self):
156         assert 'peer_id' in self
157
158         # delete relationships
159         Slices().delete.filter({'peer_id': self['peer_id']})
160         Keys().delete.filter({'peer_id': self['peer_id']})
161         Persons().delete.filter({'peer_id': self['peer_id']})
162         Nodes().delete.filter({'peer_id': self['peer_id']})
163         Sites().delete.filter({'peer_id': self['peer_id']})
164         # delete peer
165         AlchemyObj.delete(self, dict(self))
166
167     
168     def connect(self, **kwds):
169         """
170         Connect to this peer via XML-RPC.
171         """
172
173         import xmlrpclib
174         from PLC.PyCurl import PyCurlTransport
175         self.server = xmlrpclib.ServerProxy(self['peer_url'],
176                                             PyCurlTransport(self['peer_url'], self['cacert']),
177                                             allow_none = 1, **kwds)
178    
179     def add_auth(self, function, methodname, **kwds):
180         """
181         Sign the specified XML-RPC call and add an auth struct as the
182         first argument of the call.
183         """
184
185         def wrapper(*args, **kwds):
186             from PLC.GPG import gpg_sign
187             signature = gpg_sign(args,
188                                  self.api.config.PLC_ROOT_GPG_KEY,
189                                  self.api.config.PLC_ROOT_GPG_KEY_PUB,
190                                  methodname)
191
192             auth = {'AuthMethod': "gpg",
193                     'name': self.api.config.PLC_NAME,
194                     'signature': signature}
195
196             # Automagically add auth struct to every call
197             args = (auth,) + args
198
199             return function(*args)
200
201         return wrapper
202  
203     def __getattr__(self, attr):
204         """
205         Returns a callable API function if attr is the name of a
206         PLCAPI function; otherwise, returns the specified attribute.
207         """
208
209         try:
210             # Figure out if the specified attribute is the name of a
211             # PLCAPI function. If so and the function requires an
212             # authentication structure as its first argument, return a
213             # callable that automagically adds an auth struct to the
214             # call.
215             methodname = attr
216             api_function = self.api.callable(methodname)
217             if api_function.accepts and \
218                (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
219                 (isinstance(api_function.accepts[0], Mixed) and \
220                  filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
221                 function = getattr(self.server, methodname)
222                 return self.add_auth(function, methodname)
223         except Exception, err:
224             pass
225
226         if hasattr(self, attr):
227             return getattr(self, attr)
228         else:
229             raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr
230
231  
232 class Peers(list):
233     """
234     Representation of row(s) from the persons table in the
235     database.
236     """
237
238     def __init__(self, api, peer_filter = None, columns = None):
239         if not peer_filter:
240             #persons = self.api.client_shell.keystone.users.findall()
241             peers = Peer().select()
242         elif isinstance(peer_filter, (list, tuple, set)):
243             ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
244             strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
245             peer_filter = {'peer_id': ints, 'peername': strs}
246             peers = Peer().select(filter=peer_filter)
247         elif isinstance(peer_filter, dict):
248             peers = Peer().select(filter=peer_filter)
249         elif isinstance (peer_filter, StringTypes):
250             peers = Peer().select(filter={'peername': peer_filter})
251         elif isinstance (peer_filter, (int, long)):
252             peers = Peer().select(filter={'peer_id': peer_filter})
253         else:
254             raise PLCInvalidArgument, "Wrong peer filter %r"%peer_filter
255
256         for peer in peers:
257             peer = Peer(self.api, object=peer) 
258             self.append(peer)