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 # Python-2.4.z ... gah! (or even 2.3!)
20 raise ValueError, "Bad checksum type"
25 from time
import sleep
26 from certmaster
import certmaster
as certmaster
27 from func
.overlord
.client
import Client
28 from func
.CommonErrors
import Func_Client_Exception
29 import func
.jobthing
as jobthing
31 def syncable(cert_list
):
33 Calls out to known hosts to find out who is configured for
34 peering. Returns a list of hostnames who support peering.
37 fc
= Client('*', async=True, nforks
=len(cert_list
))
38 except Func_Client_Exception
:
40 # - signing the first minion
41 # - cleaning the only minion
42 # so there's nothing to hit. This shouldn't happen
43 # when we get called from the 'post-fetch' trigger
47 # Only wait for a few seconds. Assume anything that doesn't get
48 # back by then is a lost cause. Don't want this trigger to spin
51 return_code
= jobthing
.JOB_ID_RUNNING
53 job_id
= fc
.certmastermod
.peering_enabled()
54 while return_code
!= jobthing
.JOB_ID_FINISHED
and ticks
< 3:
56 (return_code
, results
) = fc
.job_status(job_id
)
60 for host
, result
in results
.iteritems():
65 def remote_peers(hosts
):
67 Calls out to hosts to collect peer information
69 fc
= Client(';'.join(hosts
))
70 return fc
.certmastermod
.known_peers()
74 Returns (hostname, sha1) hash of local certs
76 globby
= '*.%s' % cm
.cfg
.cert_extension
77 globby
= os
.path
.join(cm
.cfg
.certroot
, globby
)
81 hostname
= os
.path
.basename(f
).replace('.' + cm
.cfg
.cert_extension
, '')
83 results
.append([hostname
, digest
])
87 thissum
= hashlib
.new('sha1')
94 return thissum
.hexdigest()
96 def remove_stale_certs(local
, remote
):
98 For each cert on each remote host, make sure it exists locally.
99 If not then it has been cleaned locally and needs unlinked
102 local
= [foo
[0] for foo
in local
] # don't care about checksums
103 for host
, peers
in remote
.iteritems():
107 if peer
[0] not in local
:
110 fc
.certmastermod
.remove_peer_certs(die
)
112 def copy_updated_certs(local
, remote
):
114 For each local cert, make sure it exists on the remote with the
115 correct hash. If not, copy it over!
117 for host
, peers
in remote
.iteritems():
120 if cert
not in peers
:
121 cert_name
= '%s.%s' % (cert
[0], cm
.cfg
.cert_extension
)
122 full_path
= os
.path
.join(cm
.cfg
.certroot
, cert_name
)
126 fc
.certmastermod
.copy_peer_cert(cert
[0], xmlrpclib
.Binary(certblob
))
131 if sys
.argv
[1] in ['-f', '--force']:
136 if not cm
.cfg
.sync_certs
and not forced
:
139 certs
= glob(os
.path
.join(cm
.cfg
.certroot
,
140 '*.%s' % cm
.cfg
.cert_extension
))
141 hosts
= syncable(certs
)
144 remote
= remote_peers(hosts
)
145 local
= local_certs()
146 remove_stale_certs(local
, remote
)
147 copy_updated_certs(local
, remote
)
149 if __name__
== "__main__":
150 cm
= certmaster
.CertMaster()