4 # Thierry Parmentelat - INRIA
8 from types import StringTypes
9 from urlparse import urlparse
11 from PLC.Faults import *
12 from PLC.Parameter import Parameter, Mixed
13 from PLC.Filter import Filter
14 from PLC.Table import Row, Table
17 from PLC.Sites import Site, Sites
18 from PLC.Persons import Person, Persons
19 from PLC.Keys import Key, Keys
20 from PLC.Nodes import Node, Nodes
21 from PLC.TagTypes import TagType, TagTypes
22 from PLC.SliceTags import SliceTag, SliceTags
23 from PLC.Slices import Slice, Slices
27 Stores the list of peering PLCs in the peers table.
28 See the Row class for more details
32 primary_key = 'peer_id'
33 join_tables = ['peer_site', 'peer_person', 'peer_key', 'peer_node', 'peer_slice']
35 'peer_id': Parameter (int, "Peer identifier"),
36 'peername': Parameter (str, "Peer name"),
37 'peer_url': Parameter (str, "Peer API URL"),
38 'key': Parameter(str, "Peer GPG public key"),
39 'cacert': Parameter(str, "Peer SSL public certificate"),
40 'shortname' : Parameter(str, "Peer short name"),
41 'hrn_root' : Parameter(str, "Root of this peer in a hierarchical naming space"),
43 'site_ids': Parameter([int], "List of sites for which this peer is authoritative"),
44 'person_ids': Parameter([int], "List of users for which this peer is authoritative"),
45 'key_ids': Parameter([int], "List of keys for which this peer is authoritative"),
46 'node_ids': Parameter([int], "List of nodes for which this peer is authoritative"),
47 'slice_ids': Parameter([int], "List of slices for which this peer is authoritative"),
50 def validate_peername(self, peername):
52 raise PLCInvalidArgument, "Peer name must be specified"
54 conflicts = Peers(self.api, [peername])
55 for peer in conflicts:
56 if 'peer_id' not in self or self['peer_id'] != peer['peer_id']:
57 raise PLCInvalidArgument, "Peer name already in use"
61 def validate_peer_url(self, url):
63 Validate URL. Must be HTTPS.
66 (scheme, netloc, path, params, query, fragment) = urlparse(url)
68 raise PLCInvalidArgument, "Peer URL scheme must be https"
70 raise PLCInvalidArgument, "Peer URL should end with /"
74 def delete(self, commit = True):
76 Deletes this peer and all related entities.
79 assert 'peer_id' in self
81 # Remove all related entities
83 Slices(self.api, self['slice_ids']) + \
84 Keys(self.api, self['key_ids']) + \
85 Persons(self.api, self['person_ids']) + \
86 Nodes(self.api, self['node_ids']) + \
87 Sites(self.api, self['site_ids']):
88 assert obj['peer_id'] == self['peer_id']
89 obj.delete(commit = False)
92 self['deleted'] = True
95 def add_site(self, site, peer_site_id, commit = True):
97 Associate a local site entry with this peer.
100 add = Row.add_object(Site, 'peer_site')
102 {'peer_id': self['peer_id'],
103 'site_id': site['site_id'],
104 'peer_site_id': peer_site_id},
107 def add_person(self, person, peer_person_id, commit = True):
109 Associate a local user entry with this peer.
112 add = Row.add_object(Person, 'peer_person')
114 {'peer_id': self['peer_id'],
115 'person_id': person['person_id'],
116 'peer_person_id': peer_person_id},
119 def add_key(self, key, peer_key_id, commit = True):
121 Associate a local key entry with this peer.
124 add = Row.add_object(Key, 'peer_key')
126 {'peer_id': self['peer_id'],
127 'key_id': key['key_id'],
128 'peer_key_id': peer_key_id},
131 def add_node(self, node, peer_node_id, commit = True):
133 Associate a local node entry with this peer.
136 add = Row.add_object(Node, 'peer_node')
138 {'peer_id': self['peer_id'],
139 'node_id': node['node_id'],
140 'peer_node_id': peer_node_id},
143 def add_slice(self, slice, peer_slice_id, commit = True):
145 Associate a local slice entry with this peer.
148 add = Row.add_object(Slice, 'peer_slice')
150 {'peer_id': self['peer_id'],
151 'slice_id': slice['slice_id'],
152 'peer_slice_id': peer_slice_id},
155 def connect(self, **kwds):
157 Connect to this peer via XML-RPC.
161 from PLC.PyCurl import PyCurlTransport
162 self.server = xmlrpclib.ServerProxy(self['peer_url'],
163 PyCurlTransport(self['peer_url'], self['cacert']),
164 allow_none = 1, **kwds)
166 def add_auth(self, function, methodname, **kwds):
168 Sign the specified XML-RPC call and add an auth struct as the
169 first argument of the call.
172 def wrapper(*args, **kwds):
173 from PLC.GPG import gpg_sign
174 signature = gpg_sign(args,
175 self.api.config.PLC_ROOT_GPG_KEY,
176 self.api.config.PLC_ROOT_GPG_KEY_PUB,
179 auth = {'AuthMethod': "gpg",
180 'name': self.api.config.PLC_NAME,
181 'signature': signature}
183 # Automagically add auth struct to every call
184 args = (auth,) + args
186 return function(*args)
190 def __getattr__(self, attr):
192 Returns a callable API function if attr is the name of a
193 PLCAPI function; otherwise, returns the specified attribute.
197 # Figure out if the specified attribute is the name of a
198 # PLCAPI function. If so and the function requires an
199 # authentication structure as its first argument, return a
200 # callable that automagically adds an auth struct to the
203 api_function = self.api.callable(methodname)
204 if api_function.accepts and \
205 (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
206 (isinstance(api_function.accepts[0], Mixed) and \
207 filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
208 function = getattr(self.server, methodname)
209 return self.add_auth(function, methodname)
210 except Exception, err:
213 if hasattr(self, attr):
214 return getattr(self, attr)
216 raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr
220 Maps to the peers table in the database
223 def __init__ (self, api, peer_filter = None, columns = None):
224 Table.__init__(self, api, Peer, columns)
226 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
227 ", ".join(self.columns)
229 if peer_filter is not None:
230 if isinstance(peer_filter, (list, tuple, set)):
231 # Separate the list into integers and strings
232 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
233 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
234 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
235 sql += " AND (%s) %s" % peer_filter.sql(api, "OR")
236 elif isinstance(peer_filter, dict):
237 peer_filter = Filter(Peer.fields, peer_filter)
238 sql += " AND (%s) %s" % peer_filter.sql(api, "AND")