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