2 # Thierry Parmentelat - INRIA
6 from types import StringTypes
7 from urlparse import urlparse
9 from PLC.Faults import *
10 from PLC.Parameter import Parameter, Mixed
11 from PLC.Filter import Filter
12 from PLC.Table import Row, Table
15 from PLC.Sites import Site, Sites
16 from PLC.Persons import Person, Persons
17 from PLC.Keys import Key, Keys
18 from PLC.Nodes import Node, Nodes
19 from PLC.SliceAttributeTypes import SliceAttributeType, SliceAttributeTypes
20 from PLC.SliceAttributes import SliceAttribute, SliceAttributes
21 from PLC.Slices import Slice, Slices
25 Stores the list of peering PLCs in the peers table.
26 See the Row class for more details
30 primary_key = 'peer_id'
31 join_tables = ['peer_site', 'peer_person', 'peer_key', 'peer_node',
32 'peer_slice_attribute_type', 'peer_slice_attribute', 'peer_slice']
34 'peer_id': Parameter (int, "Peer identifier"),
35 'peername': Parameter (str, "Peer name"),
36 'peer_url': Parameter (str, "Peer API URL"),
37 'key': Parameter(str, "Peer GPG public key"),
38 'cacert': Parameter(str, "Peer SSL public certificate"),
40 'site_ids': Parameter([int], "List of sites for which this peer is authoritative"),
41 'person_ids': Parameter([int], "List of users for which this peer is authoritative"),
42 'key_ids': Parameter([int], "List of keys for which this peer is authoritative"),
43 'node_ids': Parameter([int], "List of nodes for which this peer is authoritative"),
44 'slice_ids': Parameter([int], "List of slices for which this peer is authoritative"),
47 def validate_peername(self, peername):
49 raise PLCInvalidArgument, "Peer name must be specified"
51 conflicts = Peers(self.api, [peername])
52 for peer in conflicts:
53 if 'peer_id' not in self or self['peer_id'] != peer['peer_id']:
54 raise PLCInvalidArgument, "Peer name already in use"
58 def validate_peer_url(self, url):
60 Validate URL. Must be HTTPS.
63 (scheme, netloc, path, params, query, fragment) = urlparse(url)
65 raise PLCInvalidArgument, "Peer URL scheme must be https"
69 def delete(self, commit = True):
71 Deletes this peer and all related entities.
74 assert 'peer_id' in self
76 # Remove all related entities
78 Slices(self.api, self['slice_ids'], peer_id = self['peer_id']) + \
79 Keys(self.api, self['key_ids'], peer_id = self['peer_id']) + \
80 Persons(self.api, self['person_ids'], peer_id = self['peer_id']) + \
81 Nodes(self.api, self['node_ids'], peer_id = self['peer_id']) + \
82 Sites(self.api, self['site_ids'], peer_id = self['peer_id']):
83 assert obj['peer_id'] == self['peer_id']
84 obj.delete(commit = False)
87 self['deleted'] = True
90 def add_site(self, site, peer_site_id, commit = True):
92 Associate a local site entry with this peer.
95 add = Row.add_object(Site, 'peer_site')
97 {'peer_id': self['peer_id'],
98 'site_id': site['site_id'],
99 'peer_site_id': peer_site_id},
102 def add_person(self, person, peer_person_id, commit = True):
104 Associate a local user entry with this peer.
107 add = Row.add_object(Person, 'peer_person')
109 {'peer_id': self['peer_id'],
110 'person_id': person['person_id'],
111 'peer_person_id': peer_person_id},
114 def add_key(self, key, peer_key_id, commit = True):
116 Associate a local key entry with this peer.
119 add = Row.add_object(Key, 'peer_key')
121 {'peer_id': self['peer_id'],
122 'key_id': key['key_id'],
123 'peer_key_id': peer_key_id},
126 def add_node(self, node, peer_node_id, commit = True):
128 Associate a local node entry with this peer.
131 add = Row.add_object(Node, 'peer_node')
133 {'peer_id': self['peer_id'],
134 'node_id': node['node_id'],
135 'peer_node_id': peer_node_id},
138 def add_slice_attribute_type(self, slice_attribute_type, peer_attribute_type_id, commit = True):
140 Associate a local slice attribute type entry with this peer.
143 add = Row.add_object(SliceAttributeType, 'peer_slice_attribute_type')
144 add(self, slice_attribute_type,
145 {'peer_id': self['peer_id'],
146 'attribute_type_id': slice_attribute_type['attribute_type_id'],
147 'peer_attribute_type_id': peer_attribute_type_id},
150 def add_slice_attribute(self, slice_attribute, peer_slice_attribute_id, commit = True):
152 Associate a local slice_attribute entry with this peer.
155 add = Row.add_object(SliceAttribute, 'peer_slice_attribute')
156 add(self, slice_attribute,
157 {'peer_id': self['peer_id'],
158 'slice_attribute_id': slice_attribute['slice_attribute_id'],
159 'peer_slice_attribute_id': peer_slice_attribute_id},
162 def add_slice(self, slice, peer_slice_id, commit = True):
164 Associate a local slice entry with this peer.
167 add = Row.add_object(Slice, 'peer_slice')
169 {'peer_id': self['peer_id'],
170 'slice_id': slice['slice_id'],
171 'peer_slice_id': peer_slice_id},
174 def connect(self, **kwds):
176 Connect to this peer via XML-RPC.
180 from PLC.PyCurl import PyCurlTransport
181 self.server = xmlrpclib.ServerProxy(self['peer_url'],
182 PyCurlTransport(self['peer_url'], self['cacert']),
183 allow_none = 1, **kwds)
185 def add_auth(self, function, methodname, **kwds):
187 Sign the specified XML-RPC call and add an auth struct as the
188 first argument of the call.
191 def wrapper(*args, **kwds):
192 from PLC.GPG import gpg_sign
193 signature = gpg_sign(methodname, args,
194 self.api.config.PLC_ROOT_GPG_KEY,
195 self.api.config.PLC_ROOT_GPG_KEY_PUB)
197 auth = {'AuthMethod': "gpg",
198 'name': self.api.config.PLC_NAME,
199 'signature': signature}
201 # Automagically add auth struct to every call
202 args = (auth,) + args
204 return function(*args)
208 def __getattr__(self, attr):
210 Returns a callable API function if attr is the name of a
211 PLCAPI function; otherwise, returns the specified attribute.
215 # Figure out if the specified attribute is the name of a
216 # PLCAPI function. If so and the function requires an
217 # authentication structure as its first argument, return a
218 # callable that automagically adds an auth struct to the
221 api_function = self.api.callable(methodname)
222 if api_function.accepts and \
223 (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
224 (isinstance(api_function.accepts[0], Mixed) and \
225 filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
226 function = getattr(self.server, methodname)
227 return self.add_auth(function, methodname)
228 except Exception, err:
231 if hasattr(self, attr):
232 return getattr(self, attr)
234 raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr
238 Maps to the peers table in the database
241 def __init__ (self, api, peer_filter = None, columns = None):
242 Table.__init__(self, api, Peer, columns)
244 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
245 ", ".join(self.columns)
247 if peer_filter is not None:
248 if isinstance(peer_filter, (list, tuple, set)):
249 # Separate the list into integers and strings
250 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
251 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
252 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
253 sql += " AND (%s)" % peer_filter.sql(api, "OR")
254 elif isinstance(peer_filter, dict):
255 peer_filter = Filter(Peer.fields, peer_filter)
256 sql += " AND (%s)" % peer_filter.sql(api, "AND")