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.TagTypes import TagType, TagTypes
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', 'peer_slice']
33 'peer_id': Parameter (int, "Peer identifier"),
34 'peername': Parameter (str, "Peer name"),
35 'peer_url': Parameter (str, "Peer API URL"),
36 'key': Parameter(str, "Peer GPG public key"),
37 'cacert': Parameter(str, "Peer SSL public certificate"),
39 'site_ids': Parameter([int], "List of sites for which this peer is authoritative"),
40 'person_ids': Parameter([int], "List of users for which this peer is authoritative"),
41 'key_ids': Parameter([int], "List of keys for which this peer is authoritative"),
42 'node_ids': Parameter([int], "List of nodes for which this peer is authoritative"),
43 'slice_ids': Parameter([int], "List of slices for which this peer is authoritative"),
46 def validate_peername(self, peername):
48 raise PLCInvalidArgument, "Peer name must be specified"
50 conflicts = Peers(self.api, [peername])
51 for peer in conflicts:
52 if 'peer_id' not in self or self['peer_id'] != peer['peer_id']:
53 raise PLCInvalidArgument, "Peer name already in use"
57 def validate_peer_url(self, url):
59 Validate URL. Must be HTTPS.
62 (scheme, netloc, path, params, query, fragment) = urlparse(url)
64 raise PLCInvalidArgument, "Peer URL scheme must be https"
68 def delete(self, commit = True):
70 Deletes this peer and all related entities.
73 assert 'peer_id' in self
75 # Remove all related entities
77 Slices(self.api, self['slice_ids']) + \
78 Keys(self.api, self['key_ids']) + \
79 Persons(self.api, self['person_ids']) + \
80 Nodes(self.api, self['node_ids']) + \
81 Sites(self.api, self['site_ids']):
82 assert obj['peer_id'] == self['peer_id']
83 obj.delete(commit = False)
86 self['deleted'] = True
89 def add_site(self, site, peer_site_id, commit = True):
91 Associate a local site entry with this peer.
94 add = Row.add_object(Site, 'peer_site')
96 {'peer_id': self['peer_id'],
97 'site_id': site['site_id'],
98 'peer_site_id': peer_site_id},
101 def add_person(self, person, peer_person_id, commit = True):
103 Associate a local user entry with this peer.
106 add = Row.add_object(Person, 'peer_person')
108 {'peer_id': self['peer_id'],
109 'person_id': person['person_id'],
110 'peer_person_id': peer_person_id},
113 def add_key(self, key, peer_key_id, commit = True):
115 Associate a local key entry with this peer.
118 add = Row.add_object(Key, 'peer_key')
120 {'peer_id': self['peer_id'],
121 'key_id': key['key_id'],
122 'peer_key_id': peer_key_id},
125 def add_node(self, node, peer_node_id, commit = True):
127 Associate a local node entry with this peer.
130 add = Row.add_object(Node, 'peer_node')
132 {'peer_id': self['peer_id'],
133 'node_id': node['node_id'],
134 'peer_node_id': peer_node_id},
137 def add_slice(self, slice, peer_slice_id, commit = True):
139 Associate a local slice entry with this peer.
142 add = Row.add_object(Slice, 'peer_slice')
144 {'peer_id': self['peer_id'],
145 'slice_id': slice['slice_id'],
146 'peer_slice_id': peer_slice_id},
149 def connect(self, **kwds):
151 Connect to this peer via XML-RPC.
155 from PLC.PyCurl import PyCurlTransport
156 self.server = xmlrpclib.ServerProxy(self['peer_url'],
157 PyCurlTransport(self['peer_url'], self['cacert']),
158 allow_none = 1, **kwds)
160 def add_auth(self, function, methodname, **kwds):
162 Sign the specified XML-RPC call and add an auth struct as the
163 first argument of the call.
166 def wrapper(*args, **kwds):
167 from PLC.GPG import gpg_sign
168 signature = gpg_sign(args,
169 self.api.config.PLC_ROOT_GPG_KEY,
170 self.api.config.PLC_ROOT_GPG_KEY_PUB,
173 auth = {'AuthMethod': "gpg",
174 'name': self.api.config.PLC_NAME,
175 'signature': signature}
177 # Automagically add auth struct to every call
178 args = (auth,) + args
180 return function(*args)
184 def __getattr__(self, attr):
186 Returns a callable API function if attr is the name of a
187 PLCAPI function; otherwise, returns the specified attribute.
191 # Figure out if the specified attribute is the name of a
192 # PLCAPI function. If so and the function requires an
193 # authentication structure as its first argument, return a
194 # callable that automagically adds an auth struct to the
197 api_function = self.api.callable(methodname)
198 if api_function.accepts and \
199 (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
200 (isinstance(api_function.accepts[0], Mixed) and \
201 filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))):
202 function = getattr(self.server, methodname)
203 return self.add_auth(function, methodname)
204 except Exception, err:
207 if hasattr(self, attr):
208 return getattr(self, attr)
210 raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr
214 Maps to the peers table in the database
217 def __init__ (self, api, peer_filter = None, columns = None):
218 Table.__init__(self, api, Peer, columns)
220 sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
221 ", ".join(self.columns)
223 if peer_filter is not None:
224 if isinstance(peer_filter, (list, tuple, set)):
225 # Separate the list into integers and strings
226 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
227 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
228 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
229 sql += " AND (%s) %s" % peer_filter.sql(api, "OR")
230 elif isinstance(peer_filter, dict):
231 peer_filter = Filter(Peer.fields, peer_filter)
232 sql += " AND (%s) %s" % peer_filter.sql(api, "AND")