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
, '')
82 dirname
= os
.path
.dirname(f
)
84 results
.append([hostname
, digest
, dirname
])
88 thissum
= hashlib
.new('sha1')
95 return thissum
.hexdigest()
97 def remove_stale_certs(local
, remote
):
99 For each cert on each remote host, make sure it exists locally.
100 If not then it has been cleaned locally and needs unlinked
103 local
= [foo
[0] for foo
in local
] # don't care about checksums
104 for host
, peers
in remote
.iteritems():
108 if peer
[0] not in local
:
111 fc
.certmastermod
.remove_peer_certs(die
)
113 def copy_updated_certs(local
, remote
):
115 For each local cert, make sure it exists on the remote with the
116 correct hash. If not, copy it over!
118 for host
, peers
in remote
.iteritems():
121 if cert
not in peers
:
122 cert_name
= '%s.%s' % (cert
[0], cm
.cfg
.cert_extension
)
123 full_path
= os
.path
.join(cert
[2], cert_name
)
127 fc
.certmastermod
.copy_peer_cert(cert
[0], xmlrpclib
.Binary(certblob
))
132 if sys
.argv
[1] in ['-f', '--force']:
137 if not cm
.cfg
.sync_certs
and not forced
:
140 certs
= glob(os
.path
.join(cm
.cfg
.certroot
,
141 '*.%s' % cm
.cfg
.cert_extension
))
142 hosts
= syncable(certs
)
145 remote
= remote_peers(hosts
)
146 local
= local_certs()
147 remove_stale_certs(local
, remote
)
148 copy_updated_certs(local
, remote
)
150 if __name__
== "__main__":
151 cm
= certmaster
.CertMaster()