3 # Thierry Parmentelat - INRIA
7 from types import StringTypes
8 from urlparse import urlparse
10 from PLC.Faults import *
11 from PLC.Parameter import Parameter, Mixed
12 from PLC.Filter import Filter
13 from PLC.Table import Row, Table
16 from PLC.Sites import Site, Sites
17 from PLC.Persons import Person, Persons
18 from PLC.Keys import Key, Keys
19 from PLC.Nodes import Node, Nodes
20 from PLC.TagTypes import TagType, TagTypes
21 from PLC.SliceTags import SliceTag, SliceTags
22 from PLC.Slices import Slice, Slices
26 Stores the list of peering PLCs in the peers table.
27 See the Row class for more details
31 primary_key = 'peer_id'
32 join_tables = ['peer_site', 'peer_person', 'peer_key', 'peer_node', '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"),
39 'shortname' : Parameter(str, "Peer short name"),
40 'hrn_root' : Parameter(str, "Root of this peer in a hierarchical naming space"),
42 'site_ids': Parameter([int], "List of sites for which this peer is authoritative"),
43 'person_ids': Parameter([int], "List of users for which this peer is authoritative"),
44 'key_ids': Parameter([int], "List of keys for which this peer is authoritative"),
45 'node_ids': Parameter([int], "List of nodes for which this peer is authoritative"),
46 'slice_ids': Parameter([int], "List of slices for which this peer is authoritative"),
49 def validate_peername(self, peername):
51 raise PLCInvalidArgument, "Peer name must be specified"
53 conflicts = Peers(self.api, [peername])
54 for peer in conflicts:
55 if 'peer_id' not in self or self['peer_id'] != peer['peer_id']:
56 raise PLCInvalidArgument, "Peer name already in use"
60 def validate_peer_url(self, url):
62 Validate URL. Must be HTTPS.
65 (scheme, netloc, path, params, query, fragment) = urlparse(url)
67 raise PLCInvalidArgument, "Peer URL scheme must be https"
69 raise PLCInvalidArgument, "Peer URL should end with /"
73 def delete(self, commit = True):
75 Deletes this peer and all related entities.
78 assert 'peer_id' in self
80 # Remove all related entities
82 Slices(self.api, self['slice_ids']) + \
83 Keys(self.api, self['key_ids']) + \
84 Persons(self.api, self['person_ids']) + \
85 Nodes(self.api, self['node_ids']) + \
86 Sites(self.api, self['site_ids']):
87 assert obj['peer_id'] == self['peer_id']
88 obj.delete(commit = False)
91 self['deleted'] = True
94 def add_site(self, site, peer_site_id, commit = True):
96 Associate a local site entry with this peer.
99 add = Row.add_object(Site, 'peer_site')
101 {'peer_id': self['peer_id'],
102 'site_id': site['site_id'],
103 'peer_site_id': peer_site_id},
106 def add_person(self, person, peer_person_id, commit = True):
108 Associate a local user entry with this peer.
111 add = Row.add_object(Person, 'peer_person')
113 {'peer_id': self['peer_id'],
114 'person_id': person['person_id'],
115 'peer_person_id': peer_person_id},
118 def add_key(self, key, peer_key_id, commit = True):
120 Associate a local key entry with this peer.
123 add = Row.add_object(Key, 'peer_key')
125 {'peer_id': self['peer_id'],
126 'key_id': key['key_id'],
127 'peer_key_id': peer_key_id},
130 def add_node(self, node, peer_node_id, commit = True):
132 Associate a local node entry with this peer.
135 add = Row.add_object(Node, 'peer_node')
137 {'peer_id': self['peer_id'],
138 'node_id': node['node_id'],
139 'peer_node_id': peer_node_id},
142 def add_slice(self, slice, peer_slice_id, commit = True):
144 Associate a local slice entry with this peer.
147 add = Row.add_object(Slice, 'peer_slice')
149 {'peer_id': self['peer_id'],
150 'slice_id': slice['slice_id'],
151 'peer_slice_id': peer_slice_id},
154 def connect(self, **kwds):
156 Connect to this peer via XML-RPC.
160 from PLC.PyCurl import PyCurlTransport
161 self.server = xmlrpclib.ServerProxy(self['peer_url'],
162 PyCurlTransport(self['peer_url'], self['cacert']),
163 allow_none = 1, **kwds)
165 def add_auth(self, function, methodname, **kwds):
167 Sign the specified XML-RPC call and add an auth struct as the
168 first argument of the call.
171 def wrapper(*args, **kwds):
172 from PLC.GPG import gpg_sign
173 signature = gpg_sign(args,
174 self.api.config.PLC_ROOT_GPG_KEY,
175 self.api.config.PLC_ROOT_GPG_KEY_PUB,
178 auth = {'AuthMethod': "gpg",
179 'name': self.api.config.PLC_NAME,
180 'signature': signature}
182 # Automagically add auth struct to every call
183 args = (auth,) + args
185 return function(*args)
189 def __getattr__(self, attr):
191 Returns a callable API function if attr is the name of a
192 PLCAPI function; otherwise, returns the specified attribute.
196 # Figure out if the specified attribute is the name of a
197 # PLCAPI function. If so and the function requires an
198 # authentication structure as its first argument, return a
199 # callable that automagically adds an auth struct to the
202 api_function = self.api.callable(methodname)
203 if api_function.accepts and \
204 (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
205 (isinstance(api_function.accepts[0], Mixed) and \
206 filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
207 function = getattr(self.server, methodname)
208 return self.add_auth(function, methodname)
209 except Exception, err:
212 if hasattr(self, attr):
213 return getattr(self, attr)
215 raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr
219 Maps to the peers table in the database
222 def __init__ (self, api, peer_filter = None, columns = None):
223 Table.__init__(self, api, Peer, columns)
225 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
226 ", ".join(self.columns)
228 if peer_filter is not None:
229 if isinstance(peer_filter, (list, tuple, set)):
230 # Separate the list into integers and strings
231 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
232 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
233 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
234 sql += " AND (%s) %s" % peer_filter.sql(api, "OR")
235 elif isinstance(peer_filter, dict):
236 peer_filter = Filter(Peer.fields, peer_filter)
237 sql += " AND (%s) %s" % peer_filter.sql(api, "AND")