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.
14 # Python-2.4.z ... gah! (or even 2.3!)
20 warnings
.warn('sha1 is deprecated',DeprecationWarning)
22 raise ValueError, "Bad checksum type"
27 from time
import sleep
28 from certmaster
import certmaster
as certmaster
30 func_import_failure
= None
32 from func
.overlord
.client
import Client
33 from func
.CommonErrors
import Func_Client_Exception
34 import func
.jobthing
as jobthing
35 except ImportError, e
:
36 func_import_failure
= str(e
)
38 def syncable(cert_list
):
40 Calls out to known hosts to find out who is configured for
41 peering. Returns a list of hostnames who support peering.
44 fc
= Client('*', async=True, nforks
=len(cert_list
))
45 except Func_Client_Exception
:
47 # - signing the first minion
48 # - cleaning the only minion
49 # so there's nothing to hit. This shouldn't happen
50 # when we get called from the 'post-fetch' trigger
54 # Only wait for a few seconds. Assume anything that doesn't get
55 # back by then is a lost cause. Don't want this trigger to spin
58 return_code
= jobthing
.JOB_ID_RUNNING
60 job_id
= fc
.certmastermod
.peering_enabled()
61 while return_code
!= jobthing
.JOB_ID_FINISHED
and ticks
< 3:
63 (return_code
, results
) = fc
.job_status(job_id
)
67 for host
, result
in results
.iteritems():
72 def remote_peers(hosts
):
74 Calls out to hosts to collect peer information
76 fc
= Client(';'.join(hosts
))
77 return fc
.certmastermod
.known_peers()
81 Returns (hostname, hashval) hash of local certs
83 globby
= '*.%s' % cm
.cfg
.cert_extension
84 globby
= os
.path
.join(cm
.cfg
.certroot
, globby
)
88 hostname
= os
.path
.basename(f
).replace('.' + cm
.cfg
.cert_extension
, '')
89 dirname
= os
.path
.dirname(f
)
90 digest
= checksum(f
,cm
.cfg
.hashfunc
)
91 results
.append([hostname
, digest
, dirname
])
94 def checksum(f
,hashfunc
):
95 thissum
= hashlib
.new(hashfunc
)
102 return thissum
.hexdigest()
104 def remove_stale_certs(local
, remote
):
106 For each cert on each remote host, make sure it exists locally.
107 If not then it has been cleaned locally and needs unlinked
110 local
= [foo
[0] for foo
in local
] # don't care about checksums
111 for host
, peers
in remote
.iteritems():
115 if peer
[0] not in local
:
118 fc
.certmastermod
.remove_peer_certs(die
)
120 def copy_updated_certs(local
, remote
):
122 For each local cert, make sure it exists on the remote with the
123 correct hash. If not, copy it over!
125 for host
, peers
in remote
.iteritems():
128 if cert
not in peers
:
129 cert_name
= '%s.%s' % (cert
[0], cm
.cfg
.cert_extension
)
130 full_path
= os
.path
.join(cert
[2], cert_name
)
134 fc
.certmastermod
.copy_peer_cert(cert
[0], xmlrpclib
.Binary(certblob
))
139 if sys
.argv
[1] in ['-f', '--force']:
144 if not cm
.cfg
.sync_certs
and not forced
:
147 # Don't complain about func not being available until you actually want it
148 if func_import_failure
!= None:
149 print >> sys
.stderr
, "errors importing func: %s" % func_import_failure
152 certs
= glob(os
.path
.join(cm
.cfg
.certroot
,
153 '*.%s' % cm
.cfg
.cert_extension
))
154 hosts
= syncable(certs
)
157 remote
= remote_peers(hosts
)
158 local
= local_certs()
159 remove_stale_certs(local
, remote
)
160 copy_updated_certs(local
, remote
)
162 if __name__
== "__main__":
163 cm
= certmaster
.CertMaster()