2 # Functions for interacting with the persons peers in the database
9 from types import StringTypes
11 from urlparse import urlparse
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
30 class Peer(AlchemyObj):
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().
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"),
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),
55 def validate_peer_url(self, url):
57 Validate URL. Must be HTTPS.
60 (scheme, netloc, path, params, query, fragment) = urlparse(url)
62 raise PLCInvalidArgument, "Peer URL scheme must be https"
64 raise PLCInvalidArgument, "Peer URL should end with /"
68 def validate_peername(self, peername):
71 raise PLCInvalidArgument, "Peer name must be specified"
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"
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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))
152 AlchemyObj.update(self, {'peer_id': self['peer_id']}, dict(self))
156 assert 'peer_id' in self
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']})
165 AlchemyObj.delete(self, dict(self))
168 def connect(self, **kwds):
170 Connect to this peer via XML-RPC.
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)
179 def add_auth(self, function, methodname, **kwds):
181 Sign the specified XML-RPC call and add an auth struct as the
182 first argument of the call.
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,
192 auth = {'AuthMethod': "gpg",
193 'name': self.api.config.PLC_NAME,
194 'signature': signature}
196 # Automagically add auth struct to every call
197 args = (auth,) + args
199 return function(*args)
203 def __getattr__(self, attr):
205 Returns a callable API function if attr is the name of a
206 PLCAPI function; otherwise, returns the specified attribute.
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
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:
226 if hasattr(self, attr):
227 return getattr(self, attr)
229 raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr
234 Representation of row(s) from the persons table in the
238 def __init__(self, api, peer_filter = None, columns = None):
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})
254 raise PLCInvalidArgument, "Wrong peer filter %r"%peer_filter
257 peer = Peer(self.api, object=peer)