- add peer_keys, peer_slice_attribute_types, and peer_slice_attributes
[plcapi.git] / PLC / Peers.py
1 #
2 # Thierry Parmentelat - INRIA
3
4
5 import re
6 from types import StringTypes
7 from urlparse import urlparse
8
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
13 import PLC.Auth
14
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
22
23 import xmlrpclib
24 from PLC.PyCurl import PyCurlTransport
25 from PLC.GPG import gpg_sign
26
27 class Peer(Row):
28     """
29     Stores the list of peering PLCs in the peers table. 
30     See the Row class for more details
31     """
32
33     table_name = 'peers'
34     primary_key = 'peer_id'
35     fields = {
36         'peer_id': Parameter (int, "Peer identifier"),
37         'peername': Parameter (str, "Peer name"),
38         'peer_url': Parameter (str, "Peer API URL"),
39         'key': Parameter(str, "Peer GPG public key"),
40         'cacert': Parameter(str, "Peer SSL public certificate"),
41         ### cross refs
42         'site_ids': Parameter([int], "List of sites for this peer is authoritative"),
43         'person_ids': Parameter([int], "List of users for 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         'attribute_type_ids': Parameter([int], "List of slice attribute types for which this peer is authoritative"),
47         'slice_attribute_ids': Parameter([int], "List of slice attributes for which this peer is authoritative"),
48         'slice_ids': Parameter([int], "List of slices for which this peer is authoritative"),
49         }
50
51     def validate_peer_url(self, url):
52         """
53         Validate URL. Must be HTTPS.
54         """
55
56         (scheme, netloc, path, params, query, fragment) = urlparse(url)
57         if scheme != "https":
58             raise PLCInvalidArgument, "Peer URL scheme must be https"
59
60         return url
61
62     def delete(self, commit = True):
63         """
64         Deletes this peer and all related entities.
65         """
66
67         assert 'peer_id' in self
68
69         # Remove all related entities
70         for obj in \
71                 Sites(self.api, self['site_ids']) + \
72                 Persons(self.api, self['person_ids']) + \
73                 Keys(self.api, self['key_ids']) + \
74                 Nodes(self.api, self['node_ids']) + \
75                 SliceAttributeTypes(self.api, self['attribute_type_ids']) + \
76                 SliceAttributes(self.api, self['slice_attribute_ids']) + \
77                 Slices(self.api, self['slice_ids']):
78             assert obj['peer_id'] == self['peer_id']
79             obj.delete(commit = False)
80
81         # Mark as deleted
82         self['deleted'] = True
83         self.sync(commit)
84
85     def connect(self, **kwds):
86         """
87         Connect to this peer via XML-RPC.
88         """
89
90         self.server = xmlrpclib.ServerProxy(self['peer_url'],
91                                             PyCurlTransport(self['peer_url'], self['cacert']),
92                                             allow_none = 1, **kwds)
93
94     def add_auth(self, function, methodname, **kwds):
95         """
96         Sign the specified XML-RPC call and add an auth struct as the
97         first argument of the call.
98         """
99
100         def wrapper(*args, **kwds):
101             signature = gpg_sign(methodname, args,
102                                  self.api.config.PLC_ROOT_GPG_KEY,
103                                  self.api.config.PLC_ROOT_GPG_KEY_PUB)
104
105             auth = {'AuthMethod': "gpg",
106                     'name': self.api.config.PLC_NAME,
107                     'signature': signature}
108
109             # Automagically add auth struct to every call
110             args = (auth,) + args
111
112             return function(*args)
113
114         return wrapper
115
116     def __getattr__(self, methodname):
117         """
118         Fetch a callable for the specified method.
119         """
120
121         function = getattr(self.server, methodname)
122
123         try:
124             # Figure out if the function is a PLCAPI function and
125             # requires an authentication structure as its first
126             # argument.
127             api_function = self.api.callable(methodname)
128             if api_function.accepts and \
129                (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
130                 (isinstance(api_function.accepts[0], Mixed) and \
131                  filter(lambda param: isinstance(param, Auth), func.accepts[0]))):
132                 function = self.add_auth(function, methodname)
133         except Exception, err:
134             pass
135
136         return function
137
138 class Peers (Table):
139     """ 
140     Maps to the peers table in the database
141     """
142     
143     def __init__ (self, api, peer_filter = None, columns = None):
144         Table.__init__(self, api, Peer, columns)
145
146         sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
147               ", ".join(self.columns)
148
149         if peer_filter is not None:
150             if isinstance(peer_filter, (list, tuple, set)):
151                 # Separate the list into integers and strings
152                 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
153                 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
154                 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
155                 sql += " AND (%s)" % peer_filter.sql(api, "OR")
156             elif isinstance(peer_filter, dict):
157                 peer_filter = Filter(Peer.fields, peer_filter)
158                 sql += " AND (%s)" % peer_filter.sql(api, "AND")
159
160         self.selectall(sql)