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