7b2f19d0b83eaa5cd0db6c4aa6eca3a47bd3a715
[sfa.git] / sfa / server / sfa-start.py
1 #!/usr/bin/python3 -u
2 # now that logs are managed through stdout and journalctl,
3 # it is important to run with -u so that they show up quickly
4 # and don't get buffered
5 #
6 # PlanetLab SFA implementation
7 #
8 # This implements the SFA Registry and Slice Interfaces on PLC.
9 # Depending on command line options, it starts some combination of a
10 # Registry, an Aggregate Manager, and a Slice Manager.
11 #
12 # There are several items that need to be done before starting the servers.
13 #
14 # NOTE:  Many configuration settings, including the PLC maintenance account
15 # credentials, URI of the PLCAPI, and PLC DB URI and admin credentials are initialized
16 # from your MyPLC configuration (/etc/planetlab/plc_config*).  Please make sure this information
17 # is up to date and accurate.
18 #
19 # 1) Import the existing planetlab database, creating the
20 #    appropriate SFA records. This is done by running the "sfa-import.py" tool.
21 #
22 # 2) Create a "trusted_roots" directory and place the certificate of the root
23 #    authority in that directory. Given the defaults in sfa-import-plc.py, this
24 #    certificate would be named "planetlab.gid". For example,
25 #
26 #    mkdir trusted_roots; cp authorities/planetlab.gid trusted_roots/
27 #
28 # TODO: Can all three servers use the same "registry" certificate?
29 ##
30
31 import os
32 import os.path
33 import traceback
34 import sys
35 from optparse import OptionParser
36
37 from sfa.util.sfalogging import init_logger, logger
38 from sfa.util.xrn import get_authority, hrn_to_urn
39 from sfa.util.config import Config
40
41 from sfa.trust.gid import GID
42 from sfa.trust.trustedroots import TrustedRoots
43 from sfa.trust.certificate import Keypair, Certificate
44 from sfa.trust.hierarchy import Hierarchy
45 from sfa.trust.gid import GID
46
47 from sfa.server.sfaapi import SfaApi
48 from sfa.server.registry import Registries
49 from sfa.server.aggregate import Aggregates
50
51 from sfa.client.return_value import ReturnValue
52
53
54 def install_peer_certs(server_key_file, server_cert_file):
55     """
56     Attempt to install missing trusted gids and db records for
57     our federated interfaces
58     """
59     # Attempt to get any missing peer gids
60     # There should be a gid file in /etc/sfa/trusted_roots for every
61     # peer registry found in in the registries.xml config file. If there
62     # are any missing gids, request a new one from the peer registry.
63     api = SfaApi(key_file=server_key_file, cert_file=server_cert_file)
64     registries = Registries()
65     aggregates = Aggregates()
66     interfaces = dict(list(registries.items()) + list(aggregates.items()))
67     gids_current = api.auth.trusted_cert_list
68     hrns_current = [gid.get_hrn() for gid in gids_current]
69     hrns_expected = set([hrn for hrn in interfaces])
70     new_hrns = set(hrns_expected).difference(hrns_current)
71     #gids = self.get_peer_gids(new_hrns) + gids_current
72     peer_gids = []
73     if not new_hrns:
74         return
75
76     trusted_certs_dir = api.config.get_trustedroots_dir()
77     for new_hrn in new_hrns:
78         if not new_hrn:
79             continue
80         # the gid for this interface should already be installed
81         if new_hrn == api.config.SFA_INTERFACE_HRN:
82             continue
83         try:
84             # get gid from the registry
85             url = interfaces[new_hrn].get_url()
86             interface = interfaces[new_hrn].server_proxy(
87                 server_key_file, server_cert_file, timeout=30)
88             # skip non sfa aggregates
89             server_version = api.get_cached_server_version(interface)
90             if 'sfa' not in server_version:
91                 logger.info(
92                     "get_trusted_certs: skipping non sfa aggregate: %s" % new_hrn)
93                 continue
94
95             trusted_gids = ReturnValue.get_value(interface.get_trusted_certs())
96             if trusted_gids:
97                 # the gid we want should be the first one in the list,
98                 # but lets make sure
99                 for trusted_gid in trusted_gids:
100                     # default message
101                     message = "interface: %s\t" % (api.interface)
102                     message += "unable to install trusted gid for %s" % \
103                                (new_hrn)
104                     gid = GID(string=trusted_gid)
105                     peer_gids.append(gid)
106                     if gid.get_hrn() == new_hrn:
107                         gid_filename = os.path.join(
108                             trusted_certs_dir, '%s.gid' % new_hrn)
109                         gid.save_to_file(gid_filename, save_parents=True)
110                         message = "installed trusted cert for %s" % new_hrn
111                     # log the message
112                     logger.info(message)
113         except Exception:
114             message = "interface: %s\tunable to install trusted gid for %s" % \
115                 (api.interface, new_hrn)
116             logger.log_exc(message)
117     # doesnt matter witch one
118     update_cert_records(peer_gids)
119
120
121 def update_cert_records(gids):
122     """
123     Make sure there is a record in the registry for the specified gids.
124     Removes old records from the db.
125     """
126     # import db stuff here here so this module can be loaded by PlcComponentApi
127     from sfa.storage.alchemy import global_dbsession
128     from sfa.storage.model import RegRecord
129     dbsession = global_dbsession
130     if not gids:
131         return
132     # get records that actually exist in the db
133     gid_urns = [gid.get_urn() for gid in gids]
134     hrns_expected = [gid.get_hrn() for gid in gids]
135     records_found = dbsession.query(RegRecord).\
136         filter_by(pointer=-1).filter(RegRecord.hrn.in_(hrns_expected)).all()
137
138     # remove old records
139     for record in records_found:
140         if record.hrn not in hrns_expected and \
141                 record.hrn != self.api.config.SFA_INTERFACE_HRN:
142             dbsession.delete(record)
143
144     # TODO: store urn in the db so we do this in 1 query
145     for gid in gids:
146         hrn, type = gid.get_hrn(), gid.get_type()
147         record = dbsession.query(RegRecord).filter_by(
148             hrn=hrn, type=type, pointer=-1).first()
149         if not record:
150             record = RegRecord(
151                 dict={'type': type,
152                       'hrn': hrn,
153                       'authority': get_authority(hrn),
154                       'gid': gid.save_to_string(save_parents=True),
155                       })
156             dbsession.add(record)
157     dbsession.commit()
158
159
160 def main():
161     # Generate command line parser
162     parser = OptionParser(usage="sfa-start.py [options]")
163     parser.add_option("-r", "--registry", dest="registry", action="store_true",
164                       help="run registry server", default=False)
165     parser.add_option("-a", "--aggregate", dest="am", action="store_true",
166                       help="run aggregate manager", default=False)
167     parser.add_option("-t", "--trusted-certs",
168                       dest="trusted_certs", action="store_true",
169                       help="refresh trusted certs", default=False)
170     (options, args) = parser.parse_args()
171
172     config = Config()
173     init_logger('server')
174     logger.setLevelFromOptVerbose(config.SFA_API_LOGLEVEL)
175
176     # ge the server's key and cert
177     hierarchy = Hierarchy()
178     auth_info = hierarchy.get_interface_auth_info()
179     server_key_file = auth_info.get_privkey_filename()
180     server_cert_file = auth_info.get_gid_filename()
181
182     # ensure interface cert is present in trusted roots dir
183     trusted_roots = TrustedRoots(config.get_trustedroots_dir())
184     trusted_roots.add_gid(GID(filename=server_cert_file))
185
186     if options.trusted_certs:
187         install_peer_certs(server_key_file, server_cert_file)
188
189     # start registry server
190     if (options.registry):
191         from sfa.server.registry import Registry
192         r = Registry("", config.SFA_REGISTRY_PORT,
193                      server_key_file, server_cert_file)
194         r.start()
195
196     if (options.am):
197         from sfa.server.aggregate import Aggregate
198         a = Aggregate("", config.SFA_AGGREGATE_PORT,
199                       server_key_file, server_cert_file)
200         a.start()
201
202 if __name__ == "__main__":
203     try:
204         main()
205     except Exception:
206         logger.log_exc("SFA server is exiting")
207         exit(1)