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
28 func_import_failure
= None
30 from func
.overlord
.client
import Client
31 from func
.CommonErrors
import Func_Client_Exception
32 import func
.jobthing
as jobthing
33 except ImportError, e
:
34 func_import_failure
= str(e
)
36 def syncable(cert_list
):
38 Calls out to known hosts to find out who is configured for
39 peering. Returns a list of hostnames who support peering.
42 fc
= Client('*', async=True, nforks
=len(cert_list
))
43 except Func_Client_Exception
:
45 # - signing the first minion
46 # - cleaning the only minion
47 # so there's nothing to hit. This shouldn't happen
48 # when we get called from the 'post-fetch' trigger
52 # Only wait for a few seconds. Assume anything that doesn't get
53 # back by then is a lost cause. Don't want this trigger to spin
56 return_code
= jobthing
.JOB_ID_RUNNING
58 job_id
= fc
.certmastermod
.peering_enabled()
59 while return_code
!= jobthing
.JOB_ID_FINISHED
and ticks
< 3:
61 (return_code
, results
) = fc
.job_status(job_id
)
65 for host
, result
in results
.iteritems():
70 def remote_peers(hosts
):
72 Calls out to hosts to collect peer information
74 fc
= Client(';'.join(hosts
))
75 return fc
.certmastermod
.known_peers()
79 Returns (hostname, sha1) hash of local certs
81 globby
= '*.%s' % cm
.cfg
.cert_extension
82 globby
= os
.path
.join(cm
.cfg
.certroot
, globby
)
86 hostname
= os
.path
.basename(f
).replace('.' + cm
.cfg
.cert_extension
, '')
87 dirname
= os
.path
.dirname(f
)
89 results
.append([hostname
, digest
, dirname
])
93 thissum
= hashlib
.new('sha1')
100 return thissum
.hexdigest()
102 def remove_stale_certs(local
, remote
):
104 For each cert on each remote host, make sure it exists locally.
105 If not then it has been cleaned locally and needs unlinked
108 local
= [foo
[0] for foo
in local
] # don't care about checksums
109 for host
, peers
in remote
.iteritems():
113 if peer
[0] not in local
:
116 fc
.certmastermod
.remove_peer_certs(die
)
118 def copy_updated_certs(local
, remote
):
120 For each local cert, make sure it exists on the remote with the
121 correct hash. If not, copy it over!
123 for host
, peers
in remote
.iteritems():
126 if cert
not in peers
:
127 cert_name
= '%s.%s' % (cert
[0], cm
.cfg
.cert_extension
)
128 full_path
= os
.path
.join(cert
[2], cert_name
)
132 fc
.certmastermod
.copy_peer_cert(cert
[0], xmlrpclib
.Binary(certblob
))
137 if sys
.argv
[1] in ['-f', '--force']:
142 if not cm
.cfg
.sync_certs
and not forced
:
145 # Don't complain about func not being available until you actually want it
146 if func_import_failure
!= None:
147 print >> sys
.stderr
, "errors importing func: %s" % func_import_failure
150 certs
= glob(os
.path
.join(cm
.cfg
.certroot
,
151 '*.%s' % cm
.cfg
.cert_extension
))
152 hosts
= syncable(certs
)
155 remote
= remote_peers(hosts
)
156 local
= local_certs()
157 remove_stale_certs(local
, remote
)
158 copy_updated_certs(local
, remote
)
160 if __name__
== "__main__":
161 cm
= certmaster
.CertMaster()