ba9029d20132b407469ca6d0750dd063aacb7142
[plcapi.git] / PLC / Peers.py
1 #
2 # Thierry Parmentelat - INRIA
3
4
5 import re
6 from types import StringTypes
7
8 from PLC.Faults import *
9 from PLC.Parameter import Parameter, Mixed
10 from PLC.Filter import Filter
11 from PLC.Table import Row, Table
12 import PLC.Auth
13
14 from PLC.Nodes import Nodes,Node
15 from PLC.Slices import Slices,Slice
16
17 import xmlrpclib
18 from PLC.PyCurl import PyCurlTransport
19 from PLC.GPG import gpg_sign
20
21 class Peer(Row):
22     """
23     Stores the list of peering PLCs in the peers table. 
24     See the Row class for more details
25     """
26
27     table_name = 'peers'
28     primary_key = 'peer_id'
29     fields = {
30         'peer_id' : Parameter (int, "Peer identifier"),
31         'peername' : Parameter (str, "Peer name"),
32         'peer_url' : Parameter (str, "Peer API url"),
33         'key': Parameter(str, "Peer GPG public key"),
34         'cacert': Parameter(str, "Peer SSL public certificate"),
35         ### cross refs
36         'site_ids' : Parameter ([int], "This peer's sites ids"),
37         'person_ids' : Parameter ([int], "This peer's persons ids"),
38         'node_ids' : Parameter ([int], "This peer's nodes ids"),
39         'slice_ids' : Parameter ([int], "This peer's slices ids"),
40         }
41
42     def validate_peer_url (self, url):
43         """
44         Validate URL, checks it looks like https 
45         """
46         invalid_url = PLCInvalidArgument("Invalid URL")
47         if not re.compile ("^https://.*$").match(url) : 
48             raise invalid_url
49         return url
50
51     def delete (self, commit=True):
52         """
53         Delete peer
54         """
55         
56         assert 'peer_id' in self
57
58         # remove nodes depending on this peer
59         for foreign_node in Nodes (self.api, self['node_ids']):
60             foreign_node.delete(commit)
61
62         # remove the peer
63         self['deleted'] = True
64         self.sync(commit)
65
66     def connect(self, **kwds):
67         """
68         Connect to this peer via XML-RPC.
69         """
70
71         self.server = xmlrpclib.ServerProxy(self['peer_url'],
72                                             PyCurlTransport(self['peer_url'], self['cacert']),
73                                             allow_none = 1, **kwds)
74
75     def add_auth(self, function, methodname, **kwds):
76         """
77         Sign the specified XML-RPC call and add an auth struct as the
78         first argument of the call.
79         """
80
81         def wrapper(*args, **kwds):
82             signature = gpg_sign(methodname, args,
83                                  self.api.config.PLC_ROOT_GPG_KEY,
84                                  self.api.config.PLC_ROOT_GPG_KEY_PUB)
85
86             auth = {'AuthMethod': "gpg",
87                     'name': self.api.config.PLC_NAME,
88                     'signature': signature}
89
90             # Automagically add auth struct to every call
91             args = (auth,) + args
92
93             return function(*args)
94
95         return wrapper
96
97     def __getattr__(self, methodname):
98         """
99         Fetch a callable for the specified method.
100         """
101
102         function = getattr(self.server, methodname)
103
104         try:
105             # Figure out if the function is a PLCAPI function and
106             # requires an authentication structure as its first
107             # argument.
108             api_function = self.api.callable(methodname)
109             if api_function.accepts and \
110                (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \
111                 (isinstance(api_function.accepts[0], Mixed) and \
112                  filter(lambda param: isinstance(param, Auth), func.accepts[0]))):
113                 function = self.add_auth(function, methodname)
114         except Exception, err:
115             pass
116
117         return function
118
119 class Peers (Table):
120     """ 
121     Maps to the peers table in the database
122     """
123     
124     def __init__ (self, api, peer_filter = None, columns = None):
125         Table.__init__(self, api, Peer, columns)
126
127         sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
128               ", ".join(self.columns)
129
130         if peer_filter is not None:
131             if isinstance(peer_filter, (list, tuple, set)):
132                 # Separate the list into integers and strings
133                 ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
134                 strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
135                 peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
136                 sql += " AND (%s)" % peer_filter.sql(api, "OR")
137             elif isinstance(peer_filter, dict):
138                 peer_filter = Filter(Peer.fields, peer_filter)
139                 sql += " AND (%s)" % peer_filter.sql(api, "AND")
140
141         self.selectall(sql)