fix bugs in trusted credential syncing
[sfa.git] / sfa / server / interface.py
1 #
2 ### $Id: interface.py 17583 2010-04-06 15:01:08Z tmack $
3 ### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/server/interface.py $
4 #
5
6 from sfa.util.faults import *
7 from sfa.util.storage import *
8 from sfa.util.namespace import *
9 from sfa.trust.gid import GID
10 from sfa.util.table import SfaTable
11 from sfa.util.record import SfaRecord
12 import traceback
13 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
14 import sfa.util.soapprotocol as soapprotocol
15
16  
17 # GeniLight client support is optional
18 try:
19     from egeni.geniLight_client import *
20 except ImportError:
21     GeniClientLight = None            
22
23
24 ##
25 # In is a dictionary of registry connections keyed on the registry
26 # hrn
27
28 class Interfaces(dict):
29     """
30     Interfaces is a base class for managing information on the
31     peers we are federated with. It is responsible for the following:
32
33     1) Makes sure a record exist in the local registry for the each 
34        fedeated peer   
35     2) Attepts to fetch and install trusted gids   
36     3) Provides connections (xmlrpc or soap) to federated peers
37     """
38
39     # fields that must be specified in the config file
40     default_fields = {
41         'hrn': '',
42         'addr': '', 
43         'port': '', 
44     }
45
46     # defined by the class 
47     default_dict = {}
48
49     # allowed types
50     types = ['sa', 'ma']
51
52     def __init__(self, api, conf_file, type):
53         if type not in self.types:
54             raise SfaInfaildArgument('Invalid type %s: must be in %s' % (type, self.types))    
55         dict.__init__(self, {})
56         self.api = api
57         self.type = type  
58         # load config file
59         self.interface_info = XmlStorage(conf_file, self.default_dict)
60         self.interface_info.load()
61         interfaces = self.interface_info.values()[0].values()[0]
62         if not isinstance(interfaces, list):
63             interfaces = [self.interfaces]
64         self.interfaces = {}
65         for interface in interfaces:
66             self.interfaces[interface['hrn']] = interface
67
68         print "INTERFACES", self.interfaces
69         # get connections
70         self.update(self.get_connections(self.interfaces))
71
72     def sync_interfaces(self):
73         """
74         Install missing trusted gids and db records for our federated
75         interfaces
76         """     
77         # Attempt to get any missing peer gids
78         # There should be a gid file in /etc/sfa/trusted_roots for every
79         # peer registry found in in the registries.xml config file. If there
80         # are any missing gids, request a new one from the peer registry.
81         gids_current = self.api.auth.trusted_cert_list
82         hrns_current = [gid.get_hrn() for gid in gids_current] 
83         hrns_expected = self.interfaces.keys() 
84         new_hrns = set(hrns_expected).difference(hrns_current)
85         gids = self.get_peer_gids(new_hrns)
86         # update the local db records for these registries
87         self.update_db_records(self.type, gids)
88         
89     def get_peer_gids(self, new_hrns):
90         """
91         Install trusted gids from the specified interfaces.  
92         """
93         peer_gids = []
94         if not new_hrns:
95             return peer_gids
96         trusted_certs_dir = self.api.config.get_trustedroots_dir()
97         for new_hrn in new_hrns:
98             # the gid for this interface should already be installed  
99             if new_hrn == self.api.config.SFA_INTERFACE_HRN:
100                 continue
101             try:
102                 # get gid from the registry
103                 interface_info =  self.interfaces[new_hrn]
104                 interface = self.get_connections(self.interfaces[new_hrn])[new_hrn]
105                 trusted_gids = interface.get_trusted_certs()
106                 # default message
107                 message = "interface: %s\tunable to install trusted gid for %s" % \
108                            (self.api.interface, new_hrn) 
109                 if trusted_gids:
110                     # the gid we want shoudl be the first one in the list, 
111                     # but lets make sure
112                     for trusted_gid in trusted_gids:
113                         gid = GID(string=trusted_gids[0])
114                         peer_gids.append(gid) 
115                         if gid.get_hrn() == new_hrn:
116                             gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn)
117                             gid.save_to_file(gid_filename, save_parents=True)
118                             message = "interface: %s\tinstalled trusted gid for %s" % \
119                                 (self.api.interface, new_hrn)
120                 # log the message
121                 self.api.logger.info(message)
122             except:
123                 message = "interface: %s\tunable to install trusted gid for %s" % \
124                             (self.api.interface, new_hrn) 
125                 self.api.logger.info(message)
126                 traceback.print_exc()
127         
128         # reload the trusted certs list
129         self.api.auth.load_trusted_certs()
130         return peer_gids
131
132     def update_db_records(self, type, gids):
133         """
134         Make sure there is a record in the local db for allowed registries
135         defined in the config file (registries.xml). Removes old records from
136         the db.         
137         """
138         if not gids: 
139             return
140         # get hrns we expect to find
141         # ignore records for local interfaces
142         ignore_interfaces = [self.api.config.SFA_INTERFACE_HRN]
143         hrns_expected = [gid.get_hrn() for gid in gids \
144                          if gid.get_hrn() not in ignore_interfaces]
145
146         # get hrns that actually exist in the db
147         table = SfaTable()
148         records = table.find({'type': type})
149         hrns_found = [record['hrn'] for record in records]
150        
151         # remove old records
152         for record in records:
153             if record['hrn'] not in hrns_expected:
154                 table.remove(record)
155
156         # add new records
157         for gid in gids:
158             hrn = gid.get_hrn()
159             if hrn not in hrns_found:
160                 record = {
161                     'hrn': hrn,
162                     'type': type,
163                     'pointer': -1, 
164                     'authority': get_authority(hrn),
165                     'gid': gid.save_to_string(save_parents=True),
166                 }
167                 record = SfaRecord(dict=record)
168                 table.insert(record)
169                         
170     def get_connections(self, interfaces):
171         """
172         read connection details for the trusted peer registries from file return 
173         a dictionary of connections keyed on interface hrn. 
174         """
175         connections = {}
176         required_fields = self.default_fields.keys()
177         if not isinstance(interfaces, list):
178             interfaces = [interfaces]
179         for interface in interfaces:
180             # make sure the required fields are present and not null
181             if not all([interface.get(key) for key in required_fields]):
182                 continue
183  
184             hrn, address, port = interface['hrn'], interface['addr'], interface['port']
185             url = 'http://%(address)s:%(port)s' % locals()
186             # check which client we should use
187             # sfa.util.xmlrpcprotocol is default
188             client_type = 'xmlrpcprotocol'
189             if interface.has_key('client') and \
190                interface['client'] in ['geniclientlight'] and \
191                GeniClientLight:
192                 client_type = 'geniclientlight'
193                 connections[hrn] = GeniClientLight(url, self.api.key_file, self.api.cert_file) 
194             else:
195                 connections[hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
196
197         return connections