On Tue, Sep 30, 2014 at 4:29 PM, Petr Pudlak <pud...@google.com> wrote:
> On Tue, Sep 02, 2014 at 04:19:46PM +0200, 'Helga Velroyen' via > ganeti-devel wrote: > >> This is an additional patch to the SSH patch series >> which simplifies the handling of public SSH keys by using >> the utility function WriteFile as often as possible. >> As it is a mess to merge it back into the series, >> I am sending this as an additional patch at the end of the >> series. >> >> Signed-off-by: Helga Velroyen <hel...@google.com> >> --- >> lib/ssh.py | 153 ++++++++++++++++++++++++++---- >> ------------------------------- >> 1 file changed, 66 insertions(+), 87 deletions(-) >> >> diff --git a/lib/ssh.py b/lib/ssh.py >> index a33bf99..78f2120 100644 >> --- a/lib/ssh.py >> +++ b/lib/ssh.py >> @@ -27,7 +27,6 @@ >> import logging >> import os >> import tempfile >> -import stat >> >> from functools import partial >> >> @@ -259,8 +258,7 @@ def RemoveAuthorizedKey(file_name, key): >> RemoveAuthorizedKeys(file_name, [key]) >> >> >> -def _AddPublicKeyProcessLine(new_uuid, new_key, line_uuid, line_key, >> tmp_file, >> - found): >> +def _AddPublicKeyProcessLine(new_uuid, new_key, line_uuid, line_key, >> found): >> """Processes one line of the public key file when adding a key. >> >> This is a sub function that can be called within the >> @@ -276,25 +274,20 @@ def _AddPublicKeyProcessLine(new_uuid, new_key, >> line_uuid, line_key, tmp_file, >> is processed in this function call >> @param line_key: the SSH key of the node whose line in the public key >> file is processed in this function call >> - @type tmp_file: file descriptor >> - @param tmp_file: the temporary file to which the manipulated public key >> - file is written to, before replacing the original public key file >> - automically >> @type found: boolean >> @param found: whether or not the (UUID, key) pair of the node whose key >> is being added was found in the public key file already. >> - @rtype: boolean >> - @return: a possibly updated value of C{found} >> + @rtype: (boolean, string) >> + @return: a possibly updated value of C{found} and the processed line >> >> """ >> if line_uuid == new_uuid and line_key == new_key: >> logging.debug("SSH key of node '%s' already in key file.", new_uuid) >> found = True >> - tmp_file.write("%s %s\n" % (line_uuid, line_key)) >> - return found >> + return (found, "%s %s\n" % (line_uuid, line_key)) >> >> >> -def _AddPublicKeyElse(new_uuid, new_key, tmp_file): >> +def _AddPublicKeyElse(new_uuid, new_key): >> """Adds a new SSH key to the key file if it did not exist already. >> >> This is an auxiliary function for C{_ManipulatePublicKeyFile} which >> @@ -306,16 +299,16 @@ def _AddPublicKeyElse(new_uuid, new_key, tmp_file): >> @param new_uuid: the UUID of the node whose key is added >> @type new_key: string >> @param new_key: the SSH key to be added >> - @type tmp_file: file descriptor >> - @param tmp_file: the file where the key is appended >> + @rtype: string >> + @return: a new line to be added to the file >> >> """ >> - tmp_file.write("%s %s\n" % (new_uuid, new_key)) >> + return "%s %s\n" % (new_uuid, new_key) >> >> >> def _RemovePublicKeyProcessLine( >> target_uuid, _target_key, >> - line_uuid, line_key, tmp_file, found): >> + line_uuid, line_key, found): >> """Processes a line in the public key file when aiming for removing a >> key. >> >> This is an auxiliary function for C{_ManipulatePublicKeyFile} when we >> @@ -331,22 +324,21 @@ def _RemovePublicKeyProcessLine( >> @param line_uuid: UUID of the node whose line is processed in this call >> @type line_key: string >> @param line_key: SSH key of the nodes whose line is processed in this >> call >> - @type tmp_file: file descriptor >> - @param tmp_file: temporary file which eventually replaces the ganeti >> public >> - key file >> @type found: boolean >> @param found: whether or not the UUID was already found. >> + @rtype: (boolean, string) >> + @return: a tuple, indicating if the target line was found and the >> processed >> + line; the line is 'None', if the original line is removed >> >> """ >> if line_uuid != target_uuid: >> - tmp_file.write("%s %s\n" % (line_uuid, line_key)) >> - return found >> + return (found, "%s %s\n" % (line_uuid, line_key)) >> else: >> - return True >> + return (True, None) >> >> >> def _RemovePublicKeyElse( >> - target_uuid, _target_key, _tmp_file): >> + target_uuid, _target_key): >> """Logs when we tried to remove a key that does not exist. >> >> This is an auxiliary function for C{_ManipulatePublicKeyFile} which is >> @@ -358,18 +350,17 @@ def _RemovePublicKeyElse( >> @type _target_key: string >> @param _target_key: the key of the node which was supposed to be removed >> (not used) >> - @type _tmp_file: file descriptor >> - @param _tmp_file: the temporary file which eventually will replace the >> public >> - key file (not used) >> + @rtype: string >> + @return: in this case, always None >> >> """ >> logging.debug("Trying to remove key of node '%s' which is not in list" >> " of public keys.", target_uuid) >> + return None >> >> >> def _ReplaceNameByUuidProcessLine( >> - node_name, _key, line_identifier, line_key, tmp_file, found, >> - node_uuid=None): >> + node_name, _key, line_identifier, line_key, found, node_uuid=None): >> """Replaces a node's name with its UUID on a matching line in the key >> file. >> >> This is an auxiliary function for C{_ManipulatePublicKeyFile} which >> processes >> @@ -386,25 +377,23 @@ def _ReplaceNameByUuidProcessLine( >> got replaced already or not. >> @type line_key: string >> @param line_key: SSH key of the node whose line is processed >> - @type tmp_file: file descriptor >> - @param tmp_file: temporary file which will eventually replace the >> public >> - key file >> @type found: boolean >> @param found: whether or not the line matches the node's name >> @type node_uuid: string >> @param node_uuid: the node's UUID which will replace the node name >> + @rtype: (boolean, string) >> + @return: a tuple indicating whether the target line was found and the >> + processed line >> >> """ >> if node_name == line_identifier: >> - found = True >> - tmp_file.write("%s %s\n" % (node_uuid, line_key)) >> + return (True, "%s %s\n" % (node_uuid, line_key)) >> else: >> - tmp_file.write("%s %s\n" % (line_identifier, line_key)) >> - return found >> + return (found, "%s %s\n" % (line_identifier, line_key)) >> >> >> def _ReplaceNameByUuidElse( >> - node_uuid, node_name, _key, _tmp_file): >> + node_uuid, node_name, _key): >> """Logs a debug message when we try to replace a key that is not there. >> >> This is an implementation of the auxiliary C{process_else_fn} function >> for >> @@ -418,13 +407,13 @@ def _ReplaceNameByUuidElse( >> @param node_name: the node's UUID >> @type _key: string (not used) >> @param _key: the node's SSH key (not used) >> - @type _tmp_file: file descriptor >> - @param _tmp_file: temporary file for manipulating the public key file >> - (not used) >> + @rtype: string >> + @return: in this case, always None >> >> """ >> logging.debug("Trying to replace node name '%s' with UUID '%s', but" >> " no line with that name was found.", node_name, >> node_uuid) >> + return None >> >> >> def _ParseKeyLine(line, error_fn): >> @@ -459,18 +448,16 @@ def _ManipulatePubKeyFile(target_identifier, >> target_key, >> This is a general function to manipulate the public key file. It needs >> two auxiliary functions C{process_line_fn} and C{process_else_fn} to >> work. Generally, the public key file is processed as follows: >> - 1) A temporary file is opened to write the content of the ganeti >> public key >> - file to (possibly with changes). >> 2) The function processes each line of the original ganeti public key >> file, >> - applies the C{process_line_fn} function on it, which possibly writes >> the >> - original line, a changed line or no line to the temporary file. If >> - the return value of the C{process_line_fn} function is True, it will >> - be recorded in the 'found' variable for later use. >> + applies the C{process_line_fn} function on it, which returns a possibly >> + manipulated line and an indicator whether the line in question was >> found. >> + If a line is returned, it is added to a list of lines for later writing >> + to the file. >> 3) If all lines are processed and the 'found' variable is False, the >> seconds auxiliary function C{process_else_fn} is called to possibly >> - add more lines to the temporary file. >> - 4) Finally, the temporary file is written to disk and moved to the >> original >> - files name to ensure atomic writing. >> + add more lines to the list of lines. >> + 4) Finally, the list of lines is assembled to a string and written >> + atomically to the public key file, thereby overriding it. >> > > Now the numbering goes from 2 to 4, it'd be better to renumber it to 1-3. Right, thanks for spotting that. > > > >> @type target_identifier: str >> @param target_identifier: identifier of the node whose key is added; in >> most >> @@ -494,31 +481,31 @@ def _ManipulatePubKeyFile(target_identifier, >> target_key, >> assert process_else_fn is not None >> assert process_line_fn is not None >> >> - fd_tmp, tmpname = tempfile.mkstemp(dir=os.path.dirname(key_file)) >> + old_lines = [] >> try: >> - f_tmp = os.fdopen(fd_tmp, "w") >> - try: >> - f_orig = open(key_file, "r") >> - try: >> - found = False >> - for line in f_orig: >> - (uuid, key) = _ParseKeyLine(line, error_fn) >> - if not uuid: >> - continue >> - if process_line_fn(target_identifier, target_key, uuid, >> - key, f_tmp, found): >> - found = True >> - if not found: >> - process_else_fn(target_identifier, target_key, f_tmp) >> - f_tmp.flush() >> - os.rename(tmpname, key_file) >> - finally: >> - f_orig.close() >> - finally: >> - f_tmp.close() >> - except: >> - utils.RemoveFile(tmpname) >> - raise >> + f_orig = open(key_file, "r") >> + old_lines = f_orig.readlines() >> + finally: >> + f_orig.close() >> + >> + found = False >> + new_lines = [] >> + for line in old_lines: >> + (uuid, key) = _ParseKeyLine(line, error_fn) >> + if not uuid: >> + continue >> + (new_found, new_line) = process_line_fn(target_identifier, >> target_key, >> + uuid, key, found) >> + if new_found: >> + found = True >> + if new_line is not None: >> + new_lines.append(new_line) >> + if not found: >> + new_line = process_else_fn(target_identifier, target_key) >> + if new_line is not None: >> + new_lines.append(new_line) >> + new_file_content = "".join(new_lines) >> + utils.WriteFile(key_file, data=new_file_content) >> >> >> def AddPublicKey(new_uuid, new_key, key_file=pathutils.SSH_PUB_KEYS, >> @@ -578,27 +565,19 @@ def ClearPubKeyFile(key_file=pathutils.SSH_PUB_KEYS, >> mode=0600): >> utils.WriteFile(key_file, data="", mode=mode) >> >> >> -def OverridePubKeyFile(key_map, key_file=pathutils.SSH_PUB_KEYS, >> - error_fn=errors.ProgrammerError): >> +def OverridePubKeyFile(key_map, key_file=pathutils.SSH_PUB_KEYS): >> """Overrides the public key file with a list of given keys. >> >> @type key_map: dict from str to list of str >> @param key_map: dictionary mapping uuids to lists of SSH keys >> >> """ >> - try: >> - fd_tmp, tmpname = tempfile.mkstemp(dir=os.path.dirname(key_file)) >> - f_tmp = os.fdopen(fd_tmp, "w") >> - for (uuid, keys) in key_map.items(): >> - for key in keys: >> - f_tmp.write("%s %s\n" % (uuid, key)) >> - f_tmp.flush() >> - os.rename(tmpname, key_file) >> - os.chmod(key_file, stat.S_IRUSR | stat.S_IWUSR) >> - except IOError, e: >> - raise error_fn("Cannot override key file due to error '%s'" % e) >> - finally: >> - f_tmp.close() >> + new_lines = [] >> + for (uuid, keys) in key_map.items(): >> + for key in keys: >> + new_lines.append("%s %s\n" % (uuid, key)) >> + new_file_content = "".join(new_lines) >> + utils.WriteFile(key_file, data=new_file_content) >> >> >> def QueryPubKeyFile(target_uuids, key_file=pathutils.SSH_PUB_KEYS, >> -- >> 2.1.0.rc2.206.gedb03e5 >> >> > Rest LGTM, thanks, no need to resend. > Okay. Cheers, Helga -- Helga Velroyen | Software Engineer | hel...@google.com | Google Germany GmbH Dienerstr. 12 80331 München Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Graham Law, Christine Elizabeth Flores