Keith,

I'm going to cover replying to your comments on cli.py and dc.py itself. Alok gets to deal with everything else. Lucky him!


cli.py:
37: Nit: I can't imagine this definition being useful, since you'd want to use the python builtin?

I walked all of DC and removed everything not being used by the checkpoints.


distro_const.py:

33: Nit: doesn't need an explicit import ("import os; os.path.do_stuff()" works)

Fixed


38-39: Group with other *_install imports on 44-57

41: For consistency (with e.g., line 62's use of logging.Formatter), use logging.StreamHandler instead of importing the class separately.

42: Group with the other stdlib imports on 30-36

52: Combine with line 50.

All of the imports have been fixed. NOTE: I typically use the PEP8 standard on imports:

      Imports should be grouped in the following order:

      1. standard library imports
      2. related third party imports
      3. local application/library specific imports


Take another look and let me know if you have further issues with the imports.


67-68: Since nothing is done here but a call to the parent class, just skip overriding it.

Removed.


133: Have we considered deprecating the need for including "build" as part of the command? If that's not in the cards, have we considered having a second, separate subcommand for list (rather than 'build -l', which doesn't actually build anything)?

We need to talk to the CLI folks before we can make a change like this. Alok will take care of it, but for now it's still using build -l for listing checkpoints.


133: Could just be "if 'build' not in args:" - I believe OptionParser always returns at least an empty list for args? Should also check to make sure "build" is the first item.

All of the argument stuff has been reworked to be a lot smarter.


137-138: Between this and the previous line, it looks like if you specified: "distro_const build" the error message you'd get is "build is not a valid manifest", which seems off. Similarly, "distro_const build one two three" appears to be valid. I think the length of args needs to be explicitly checked.

Fixed.


138: Requiring the file to end in ".xml" seems decidedly "Windows-like" to me. I can store XML data in a file by any name; why must it end in .xml? Let ManifestParser choke on it if the file doesn't *contain* valid XML.

Fixed.


149: Use of global variables is disconcerting, particularly since they're not in ALL_CAPS form. (Once I get to the EOF I may have a suggestion or two on how to refactor appropriately.)

There are still a handful of globals, but they're now done properly (ALL CAPS). This specific issue has been addressed by explicitly passing the variable.


152: On the *rare* chance that this file can't be remo
try:
    os.remove(lf_path)
except BaseException as err:
    print "Could not remove '%s' because: %s" % (lf_path, err)

Fixed.


Since this code needs to run

155: DC doesn't need the default log created by the InstallLogger. This tells me that creating the default log automatically, without having an option to *not* create it, is a mistake. There should probably be a flag to the logging module (and/or the engine) to skip using the default log (the flag can default to either True or False, but I think there should be one).

*ALL* of the logging code has been dramatically changed.

- We only log one file now (instead of two).
- We use the default log file as provided by the logging module
- I wrote a method to transfer the log in logger.py, thusly removing the need for the transfer_logs() function


172: Would be *great* if we could add a command-line flag to modify the verbosity of the stdout handler.

Done. -v/--verbose now sets the screen log level to .DEBUG. Default is .INFO still.


184: It'd be nice to see part of this code as a method of DCFileHandler (close stream, change file location, re-open stream and flush pending items). Bonus points if this class moves to the logging module proper and gets integrated with the transfer_logs method there...

Class has been completely removed.  See above for logging changes.


184: Unused variable - dataset (globals again!)

Gone.  No globals anyway :)


195: Should be set to logging.INFO, though if this could be refactored per the above comment (so that, instead of creating a new handler, the old one is re-used), that won't be needed.

Contained withing logging changes.


216-219, 221: As I recall from DOC behavior, each function will either return None or raise an exception for the "not found" case - so only one of these should be needed. If an exception is raised, do you really need this try/except clause? If None is returned, you should raise an exception on 222/223 instead of calling sys.exit.

227: If the schema prevents this, can you just use the "get_first_child" syntax of the DOC instead of using the prior logic?

234: If the schema doesn't prevent this, then remove the mention of DOC and explain what's wrong with the XML. If the schema DOES prevent this, then don't try/except (since it would occur as a programming error, and the developer that introduces a bug causing it might find the full stacktrace more valuable).

All addressed by moving to get_first_child() and a small update to dc.dtd (forced the manifest to have one and only one Execution section).


237: Shouldn't this be:
eng.stop_on_error = (execution_list[0].stop_on_error.capitalize() == "True")
The engine expects a boolean, not a string.

Fixed.


250: Might be better as:
<logger>.exception("Error registering checkpoint <...>. Full traceback data is available in the log." Since custom checkpoints are feasible, if a user adds their own custom checkpoint, they'll probably appreciate getting the full traceback information.

Fixed.


253: I don't think you want to remove the lockfile at this point in DC's execution, do you? (Seems like you'd want to hold the lock until right before DC exits in most cases - a try/finally clause around main() might be best)

The lockfile mechanics have ALSO been totally redone as a context manager. Huzzah Python!



279: If there's more than 1 error in the error service, it should be retrieved and dumped to a log at the minimum, I think.

Fixed to dump as many errors as recorded in the error service.


281: This code isn't in an "except" clause, so logger.error/critical should be used.

Fixed.


282-288: Should *not* be part of the 'for' loop. (Don't need to tell them where the log file is for *every* failed checkpoint).

Fixed.


284: Is there supposed to be an else clause here that prints out the location of the logs when the base_dataset (global!) is set?

The printout of the final location of the logs happens after setup_build_datasets(), once DC has either created the needed datasets or has found them already in existence. Also, global removed.


282, 286, 288: Should use logger.critical/error for consistency with other statements in this clause (consider what happens when the log level is set to something other than the default).

Fixed.  Moved to .info()


306-310 (and elsewhere): When using format strings with logging statements, please do the following:
- Have the logging code apply the formatting by using the form:
        logger.debug("some format %s", msg)
    (i.e., pass in the format parameters as arguments to the logger call)
- Do not call str(some_object). ("%s" will automatically call str() on the object passed in) - If a function is called to get data for the output string, enclose the call in a statement of the form: "if logger.isEnabledFor(logging.<level>):"

While these enhancements are not strictly necessary at the moment, they are best practices when it comes to logging and thus good habits to follow. They allow the logging module to completely skip the relatively performance intensive steps of formatting in situations when the logging message will be ignored due to the logger levels being above the statement (i.e., if logger's level is logging.ERROR, format strings won't be processed for logging.debug() statements).

I'm not real sure what you mean by this.  Let's speak offline.


307, 309: Are both data dumps needed and valuable? Seems like they could clutter the detailed log.

Having both data dumps has been invaluable for debugging where I screwed up the DataObject subclasses in DC. I'd prefer to leave both in the debug log.


313: The amount of logic here makes me wonder if perhaps there's some way to make better use of, or enhance, the engine to help out. Perhaps DC could "inject" a checkpoint called "empty" at the beginning of the checkpoint lists (i.e., it goes after Manifest Parser and Target Instantiation, but before any user-defined checkpoints).

There has ALSO been a sizable rewrite of the "get mountpoint" code in dc.py. We now have a _get_mountpoints function to retrieve them. The resulting code has slimmed down dc.py considerably.



330: "if not execute:"

Fixed.


345-348: Seems like this should be an "acquire_lockfile()" function counterpart to remove_lockfile()

Context manager takes care of the acquire() portion.

346-7: Using logging and raising an exception would be preferred here.

Fixed.


350-355: Combine to single "if" clause, and then combine 358-365 to:

build_data_node.action = base_action
logs_node.action = base_action
media_node.action = base_action

Fixed.


439: I'm finding that it's generally better to use the following paradigm for python executables:

def main():
    # do stuff

if __name__ == '__main__':
    main()

You'll find that using that set-up will break your global variables (being in the "if" clause means your in the global scope; relocating that to a function constrains the scope in a way that's more obvious to anyone maintaining the code). The only variables in this code that should be global are DC_LOGGER and maybe the ENGINE.

Fixed. 99% of distro_const.py has been relocated into __init__.py What was the "if __name__ == '__main__': section is now a main() function in __init__.py


467: Seems unnecessary? If there is some situation where the error service already has an error at this point, it'd certainly be bad to clear it out.

The error service stuff is cleared first thing into walking into main().


492ish: It appears that, based on the current schema and usage of target instantiation, one could write a DC manifest that "creates" rpool, or one that "creates" a new, second pool. Those seem like edge case scenarios, and in both cases could have disastrous consequences - a combination that lends itself to either disallowing those possibilities, requiring a "-f" flag, or *prompting* the user to confirm that they really want to do such a thing, before continuing.

This is DC, not a generic zpool / ZFS dataset generator (as evidenced by such things as limiting the number of pools to 1, and number of datasets to 1). Certainly, one could use the engine with the TI checkpoint to create a simple, XML driven pool/dataset maker, but I don't think that's a requirement here.

497-506: Use:
zpool_ds = zfs_lib.Dataset(zpool_name)
zpool_mp = zpool_ds.mountpoint
(You may need to catch AttributeError if there's no guarantee of the dataset being existent)

509: I think recreating the logic that zfs uses to determine the default mountpoint for a non-existent dataset is extremely fragile at best. For example, with the existing logic, if the user has the following datasets:
rpool
rpool/mystuff (mountpoint = /export/me/mystuff)
If the user gives DC "rpool/mystuff/dc_build" as a dataset, it looks like DC will think the mountpoint is going to be /rpool/mystuff/dc_build, when in reality, it will be /export/me/mystuff/dc_build.

I think the best approach will be to wait until the target instantiation step is complete, then get the mountpoint data based on what the actual dataset mountpoints are. Any processing based on mountpoints that's done before then will need to be refactored - either moved to a later point, or adjusted to consider whether or not the parent DC build dataset exists yet.

Considerable code has gone into DC to fix all of these issues including your suggested method for retrieving the dataset's mountpoint.


515-524: I like how clean the "if" side of this if/else is. Could the "else" portion be refactored into a separate function like list_checkpoints is?

With all the refactoring, It's now ~ a dozen python statements. Take a look and if it's still not up to snuff, we can refactor again.


546: Come to think of it, "set_http_proxy()" might be valuable in other installers too (I'm thinking AI in particular). Can it be moved somewhere common?

Moved to install_utils.


565-6: This assignment was already done towards the top of the main section.
570-1: Ditto

Fixed.


572-576: Is there any reason to not let the engine do this check for you? If there is a reason, it probably means the engine could use a slight enhancement.

Fixed.  The engine now tells us when a checkpoint doesn't exist to pause at.


588: RuntimeError *is* a BaseException (everything is a BaseException). Also, nit, use "as msg" instead of ", msg"

Fixed.


Other:

No unit tests directly against any of the functions in distro_const.py?

Will be there shortly.  Needed to refactor dc.py for changes first.

Thanks again, Keith!

-Drew

_______________________________________________
caiman-discuss mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/caiman-discuss

Reply via email to