Revision a6c43c02 lib/cmdlib/cluster.py
b/lib/cmdlib/cluster.py | ||
---|---|---|
21 | 21 |
|
22 | 22 |
"""Logical units dealing with the cluster.""" |
23 | 23 |
|
24 |
import OpenSSL |
|
25 |
|
|
26 | 24 |
import copy |
27 | 25 |
import itertools |
28 | 26 |
import logging |
... | ... | |
1484 | 1482 |
""" |
1485 | 1483 |
|
1486 | 1484 |
ETYPE_FIELD = "code" |
1487 |
ETYPE_ERROR = "ERROR"
|
|
1488 |
ETYPE_WARNING = "WARNING"
|
|
1485 |
ETYPE_ERROR = constants.CV_ERROR
|
|
1486 |
ETYPE_WARNING = constants.CV_WARNING
|
|
1489 | 1487 |
|
1490 | 1488 |
def _Error(self, ecode, item, msg, *args, **kwargs): |
1491 | 1489 |
"""Format an error message. |
... | ... | |
1529 | 1527 |
self._Error(*args, **kwargs) |
1530 | 1528 |
|
1531 | 1529 |
|
1532 |
def _VerifyCertificate(filename): |
|
1533 |
"""Verifies a certificate for L{LUClusterVerifyConfig}. |
|
1534 |
|
|
1535 |
@type filename: string |
|
1536 |
@param filename: Path to PEM file |
|
1537 |
|
|
1538 |
""" |
|
1539 |
try: |
|
1540 |
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, |
|
1541 |
utils.ReadFile(filename)) |
|
1542 |
except Exception, err: # pylint: disable=W0703 |
|
1543 |
return (LUClusterVerifyConfig.ETYPE_ERROR, |
|
1544 |
"Failed to load X509 certificate %s: %s" % (filename, err)) |
|
1545 |
|
|
1546 |
(errcode, msg) = \ |
|
1547 |
utils.VerifyX509Certificate(cert, constants.SSL_CERT_EXPIRATION_WARN, |
|
1548 |
constants.SSL_CERT_EXPIRATION_ERROR) |
|
1549 |
|
|
1550 |
if msg: |
|
1551 |
fnamemsg = "While verifying %s: %s" % (filename, msg) |
|
1552 |
else: |
|
1553 |
fnamemsg = None |
|
1554 |
|
|
1555 |
if errcode is None: |
|
1556 |
return (None, fnamemsg) |
|
1557 |
elif errcode == utils.CERT_WARNING: |
|
1558 |
return (LUClusterVerifyConfig.ETYPE_WARNING, fnamemsg) |
|
1559 |
elif errcode == utils.CERT_ERROR: |
|
1560 |
return (LUClusterVerifyConfig.ETYPE_ERROR, fnamemsg) |
|
1561 |
|
|
1562 |
raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode) |
|
1563 |
|
|
1564 |
|
|
1565 | 1530 |
def _GetAllHypervisorParameters(cluster, instances): |
1566 | 1531 |
"""Compute the set of all hypervisor parameters. |
1567 | 1532 |
|
... | ... | |
1642 | 1607 |
feedback_fn("* Verifying cluster certificate files") |
1643 | 1608 |
|
1644 | 1609 |
for cert_filename in pathutils.ALL_CERT_FILES: |
1645 |
(errcode, msg) = _VerifyCertificate(cert_filename)
|
|
1610 |
(errcode, msg) = utils.VerifyCertificate(cert_filename)
|
|
1646 | 1611 |
self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode) |
1647 | 1612 |
|
1648 | 1613 |
self._ErrorIf(not utils.CanRead(constants.LUXID_USER, |
... | ... | |
2328 | 2293 |
" should node %s fail (%dMiB needed, %dMiB available)", |
2329 | 2294 |
self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree) |
2330 | 2295 |
|
2296 |
def _VerifyClientCertificates(self, nodes, all_nvinfo): |
|
2297 |
"""Verifies the consistency of the client certificates. |
|
2298 |
|
|
2299 |
This includes several aspects: |
|
2300 |
- the individual validation of all nodes' certificates |
|
2301 |
- the consistency of the master candidate certificate map |
|
2302 |
- the consistency of the master candidate certificate map with the |
|
2303 |
certificates that the master candidates are actually using. |
|
2304 |
|
|
2305 |
@param nodes: the list of nodes to consider in this verification |
|
2306 |
@param all_nvinfo: the map of results of the verify_node call to |
|
2307 |
all nodes |
|
2308 |
|
|
2309 |
""" |
|
2310 |
candidate_certs = self.cfg.GetClusterInfo().candidate_certs |
|
2311 |
if candidate_certs is None or len(candidate_certs) == 0: |
|
2312 |
self._ErrorIf( |
|
2313 |
True, constants.CV_ECLUSTERCLIENTCERT, None, |
|
2314 |
"The cluster's list of master candidate certificates is empty." |
|
2315 |
"If you just updated the cluster, please run" |
|
2316 |
" 'gnt-cluster renew-crypto --new-node-certificates'.") |
|
2317 |
return |
|
2318 |
|
|
2319 |
self._ErrorIf( |
|
2320 |
len(candidate_certs) != len(set(candidate_certs.values())), |
|
2321 |
constants.CV_ECLUSTERCLIENTCERT, None, |
|
2322 |
"There are at least two master candidates configured to use the same" |
|
2323 |
" certificate.") |
|
2324 |
|
|
2325 |
# collect the client certificate |
|
2326 |
for node in nodes: |
|
2327 |
if node.offline: |
|
2328 |
continue |
|
2329 |
|
|
2330 |
nresult = all_nvinfo[node.uuid] |
|
2331 |
if nresult.fail_msg or not nresult.payload: |
|
2332 |
continue |
|
2333 |
|
|
2334 |
(errcode, msg) = nresult.payload.get(constants.NV_CLIENT_CERT, None) |
|
2335 |
|
|
2336 |
self._ErrorIf( |
|
2337 |
errcode is not None, constants.CV_ECLUSTERCLIENTCERT, None, |
|
2338 |
"Client certificate of node '%s' failed validation: %s (code '%s')", |
|
2339 |
node.uuid, msg, errcode) |
|
2340 |
|
|
2341 |
if not errcode: |
|
2342 |
digest = msg |
|
2343 |
if node.master_candidate: |
|
2344 |
if node.uuid in candidate_certs: |
|
2345 |
self._ErrorIf( |
|
2346 |
digest != candidate_certs[node.uuid], |
|
2347 |
constants.CV_ECLUSTERCLIENTCERT, None, |
|
2348 |
"Client certificate digest of master candidate '%s' does not" |
|
2349 |
" match its entry in the cluster's map of master candidate" |
|
2350 |
" certificates. Expected: %s Got: %s", node.uuid, |
|
2351 |
digest, candidate_certs[node.uuid]) |
|
2352 |
else: |
|
2353 |
self._ErrorIf( |
|
2354 |
True, constants.CV_ECLUSTERCLIENTCERT, None, |
|
2355 |
"The master candidate '%s' does not have an entry in the" |
|
2356 |
" map of candidate certificates.", node.uuid) |
|
2357 |
self._ErrorIf( |
|
2358 |
digest in candidate_certs.values(), |
|
2359 |
constants.CV_ECLUSTERCLIENTCERT, None, |
|
2360 |
"Master candidate '%s' is using a certificate of another node.", |
|
2361 |
node.uuid) |
|
2362 |
else: |
|
2363 |
self._ErrorIf( |
|
2364 |
node.uuid in candidate_certs, |
|
2365 |
constants.CV_ECLUSTERCLIENTCERT, None, |
|
2366 |
"Node '%s' is not a master candidate, but still listed in the" |
|
2367 |
" map of master candidate certificates.", node.uuid) |
|
2368 |
self._ErrorIf( |
|
2369 |
(node.uuid not in candidate_certs) and |
|
2370 |
(digest in candidate_certs.values()), |
|
2371 |
constants.CV_ECLUSTERCLIENTCERT, None, |
|
2372 |
"Node '%s' is not a master candidate and is incorrectly using a" |
|
2373 |
" certificate of another node which is master candidate.", |
|
2374 |
node.uuid) |
|
2375 |
|
|
2331 | 2376 |
def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo, |
2332 | 2377 |
(files_all, files_opt, files_mc, files_vm)): |
2333 | 2378 |
"""Verifies file checksums collected from all nodes. |
... | ... | |
2371 | 2416 |
if nresult.fail_msg or not nresult.payload: |
2372 | 2417 |
node_files = None |
2373 | 2418 |
else: |
2374 |
fingerprints = nresult.payload.get(constants.NV_FILELIST, None)
|
|
2419 |
fingerprints = nresult.payload.get(constants.NV_FILELIST, {})
|
|
2375 | 2420 |
node_files = dict((vcluster.LocalizeVirtualPath(key), value) |
2376 | 2421 |
for (key, value) in fingerprints.items()) |
2377 | 2422 |
del fingerprints |
... | ... | |
3008 | 3053 |
constants.NV_OSLIST: None, |
3009 | 3054 |
constants.NV_VMNODES: self.cfg.GetNonVmCapableNodeList(), |
3010 | 3055 |
constants.NV_USERSCRIPTS: user_scripts, |
3056 |
constants.NV_CLIENT_CERT: None, |
|
3011 | 3057 |
} |
3012 | 3058 |
|
3013 | 3059 |
if vg_name is not None: |
... | ... | |
3132 | 3178 |
|
3133 | 3179 |
feedback_fn("* Verifying configuration file consistency") |
3134 | 3180 |
|
3181 |
self._VerifyClientCertificates(self.my_node_info.values(), all_nvinfo) |
|
3135 | 3182 |
# If not all nodes are being checked, we need to make sure the master node |
3136 | 3183 |
# and a non-checked vm_capable node are in the list. |
3137 | 3184 |
absent_node_uuids = set(self.all_node_info).difference(self.my_node_info) |
Also available in: Unified diff