3 # Syncs the valid CA-signed certificates from certmaster to all known
4 # hosts via func. To be called during the post-sign hook to copy new
5 # certificates and from the post-clean hook in order to purge stale
6 # certificates. Requires 'sync_certs' to be set in certmaster.conf.
13 from time
import sleep
14 from certmaster
import certmaster
as certmaster
15 from func
.overlord
.client
import Client
16 from func
.CommonErrors
import Func_Client_Exception
17 import func
.jobthing
as jobthing
19 def syncable(cert_list
):
21 Calls out to known hosts to find out who is configured for
22 peering. Returns a list of hostnames who support peering.
25 fc
= Client('*', async=True, nforks
=len(cert_list
))
26 except Func_Client_Exception
:
28 # - signing the first minion
29 # - cleaning the only minion
30 # so there's nothing to hit. This shouldn't happen
31 # when we get called from the 'post-fetch' trigger
35 # Only wait for a few seconds. Assume anything that doesn't get
36 # back by then is a lost cause. Don't want this trigger to spin
39 return_code
= jobthing
.JOB_ID_RUNNING
41 job_id
= fc
.certmastermod
.peering_enabled()
42 while return_code
!= jobthing
.JOB_ID_FINISHED
and ticks
< 3:
44 (return_code
, results
) = fc
.job_status(job_id
)
48 for host
, result
in results
.iteritems():
53 def remote_peers(hosts
):
55 Calls out to hosts to collect peer information
57 fc
= Client(';'.join(hosts
))
58 return fc
.certmastermod
.known_peers()
62 Returns (hostname, sha1) hash of local certs
64 globby
= '*.%s' % cm
.cfg
.cert_extension
65 globby
= os
.path
.join(cm
.cfg
.certroot
, globby
)
69 hostname
= os
.path
.basename(f
).replace('.' + cm
.cfg
.cert_extension
, '')
71 results
.append([hostname
, digest
])
82 return thissum
.hexdigest()
84 def remove_stale_certs(local
, remote
):
86 For each cert on each remote host, make sure it exists locally.
87 If not then it has been cleaned locally and needs unlinked
90 local
= [foo
[0] for foo
in local
] # don't care about checksums
91 for host
, peers
in remote
.iteritems():
95 if peer
[0] not in local
:
98 fc
.certmastermod
.remove_peer_certs(die
)
100 def copy_updated_certs(local
, remote
):
102 For each local cert, make sure it exists on the remote with the
103 correct hash. If not, copy it over!
105 for host
, peers
in remote
.iteritems():
108 if cert
not in peers
:
109 cert_name
= '%s.%s' % (cert
[0], cm
.cfg
.cert_extension
)
110 full_path
= os
.path
.join(cm
.cfg
.certroot
, cert_name
)
114 fc
.certmastermod
.copy_peer_cert(cert
[0], xmlrpclib
.Binary(certblob
))
119 if sys
.argv
[1] in ['-f', '--force']:
124 if not cm
.cfg
.sync_certs
and not forced
:
127 certs
= glob(os
.path
.join(cm
.cfg
.certroot
,
128 '*.%s' % cm
.cfg
.cert_extension
))
129 hosts
= syncable(certs
)
132 remote
= remote_peers(hosts
)
133 local
= local_certs()
134 remove_stale_certs(local
, remote
)
135 copy_updated_certs(local
, remote
)
137 if __name__
== "__main__":
138 cm
= certmaster
.CertMaster()