Hi Jean, Thank you for the clarification on the phone yesterday. I think we came to a consensus on all the various points of the code review. All changes we discussed were made. I think the biggest issues comes to removing files in the individual functions which find the files versus removing them all at one in a list (as is done). Pros to removing them in each function: *Files are removed immediately *No return from the function has to be dealt with (eases debugging to some)
Pros to removing them in a list: *Feedback to user is separated into two stages: Finding files and Removing files *List of files to delete provides a clean way to debug ordering of file removal and seeing all files which are to be removed (akin to a dry-run) I think we came to an agreement that both approaches are closely equal in usefulness and largely a stylistic difference. Thank you, Clay On Wed, 30 Sep 2009, jeanm wrote: > Clay Baenziger wrote: >> Hi Jean, >> Thank you for the review! My comments are in-line. >> Thank you, >> Clay >> >> On Mon, 28 Sep 2009, jeanm wrote: >> >>> >>> I found time to do this when I thought I wouldn't. Using the following >>> webrev: >>> http://cr.opensolaris.org/~clayb/delete_service/webrev2. I haven't checked >>> to see >>> if others have commented on these items. Pardon me if they have. >>> >>> Also, if you just want to discuss this in the office instead of an email >>> thread I'm fine with that. >>> Sometimes it's easier. >>> >>> Jean >>> >>> >>> usr/src/cmd/installadm/delete_client.py.html >>> >>> General: >>> This is the delete-client file but there are references to delete-service >>> throughout. That causes me to be confused >>> since I know they are different and have different functionality. Is this >>> a cut and paste issue? Or similar functionality >>> between the two? >> >> I only see delete_service mentioned when the delete_service module is >> called for various functions. This is a shared code approach as >> delete_client is a subset of the functionality exported by delete_service. > And line 26 which is a general overview of the file. The comment you just > made might be nice somewhere since it does explain why > delete-service is in the delete-client code. > >> >>> line 78-81: Since you're hardcoding the first %s to installadm why bother >>> with the %s in the first place? I actually >>> don't think you even need the 2nd %s. This is run in delete-client. If >>> prog is something other than that, this message >>> would be extremely confusing. >> >> This is just to make it more modular as these commands will hopefully all >> collapse down to installadm as soon as create-service becomes Python. Until >> then this just prevents hard coding bits. Yes, prog should be delete-client >> actually to mimick how delete-client reports errors today. > This makes sense then. If eventually it will be just one function call with > parameters then I'm fine with leaving it in for now. >> >> This is achieved using the /usr/lib/installadm/delete-client symlink which >> points to /usr/lib/installadm/delete_client.py. The reason for printing the >> program name is just since it has these two execution possibilities. The >> reason for having the delete_client is so that the module is importable by >> Python (modules needing to be [a-zA-Z0-9_].py. >> >>> line 43: Wouldn't it be more helpful to say a tuple representing.... >> >> You want me to be helpful now, that's just too much! Thanks for the catch, >> I've changed it to: >> >> Returns: A tuple of a class object representing client to delete >> and an options object > Thanks. >> >>> line 62: Why did you hang this off of options? >> >> This is an option the functions expect to be set. This comes from >> delete_service being able to delete an image, so as the comment says: >> "we do not deleteImage for a delete-client so set it False" > So you're saying that the delete-service functionality might need this, > correct? In which case I'm fine with it. >> >> Further, looking at line 89 (the only place options is really used) we see >> that indeed deleteImage is used there, and we can correlate from line 60 >> (the comment) image deletion is hard coded off. > But if this was really the only reason we would just return a boolean. If > however as stated above, delete-service would use it then you have it right. >> >> Note: I swapped line 60 (the comment for line 62) and line 61. I >> accidentally got them out of order. >> >>> usr/src/cmd/installadm/delete_service.py.html >>> >>> lines 837-840 : same comment as lines 78-81 in delete-client.py >> >> Same reasons as with delete_client >> >>> line 64: this really returns a tuple. Same comment as line 62 of >>> delete_client.py >> >> Same fix as delete_client >> >>> General comment: I find that putting comments into the middle of a group >>> of >>> code that is line continued makes the code difficult to read. >>> Ex. >>> >>> 377 files = [image_path, >>> 378 # must strip the leading path separator from >>> image_path as >>> 379 # os.join won't concatenate two absolute paths >>> 380 os.path.join('/var/ai/image-server/images/', >>> 381 image_path.lstrip(os.sep))] >> >> Hrm, I know we talked about this. I find this breaks up the logic so I can >> see the intention behind it. I see the list files consists of only two >> elements: >> image_path >> os.path.join('var/ai/image-server/images/',image_path.strip(os.sep)) >> >> Further, following better formatting allowed by PEP8 I think this should be >> formatted as follows which more clearly shows that it's two separate >> elements in the list: >> files = [image_path, >> # must strip the leading path separator from image_path as >> # os.join won't concatenate two absolute paths >> os.path.join('/var/ai/image-server/images/', >> image_path.lstrip(os.sep))] >> >> Also, do you use an editor or viewer which does syntax highlighting? I just >> realized since my vi is configured for syntax highlighting, comments are >> lighter than code, so I don't even visually notice them -- unless I >> question what the code's doing and this lessens the distraction immensely. > No and the code review utility didn't either. In this particular case I'm > fine with leaving this. Other cases it becomes much more distracting. Usually > where you have multi line list comprehensions. >> >>> Another general comment. There are lots of hard coded paths. This makes >>> changes >>> to these paths in the future very difficult to maintain in the code. >> >> Yes, I'm not fond of them. This is tracked in 4402 (Pull fixed strings in >> A/I server python code out to a separate module) which is a holder for >> finding an elegant way to pull out these various paths. It seems we should >> have a module we import for various install components and their paths but >> perhaps there's better or different -- it's too big to design that here >> though. > Great. Thanks. >> >>> line 196: >>> It would be cleaner to just do >>> if not return_val >>> return 1 >>> return return_val >>> >>> But you should be sure that return_val is None at the top. Certainly is >>> easier to read and cleaner. >> >> This was accidentally left in from some tinkering I could've sworn I'd >> gotten them. >> >> This is further incredibly inexcusable, as I'm returning -1, 0, 1 which is >> asinine in the days of Python. It should have been something transparently >> obvious to a developer and user (e.g. "failure", "okay", "human assistance >> needed"). I've now ensured these returns are stripped from stop_service() >> and remove_DHCP_macro(). > Thanks. >> >>> line 403-425: Seems like there should be a lot simpler way of just >>> removing a line from vfstab. >>> Is there? >> >> Um, this seems pretty short? >> >> We've gotta open the file and handle the file not existing, opening, etc. >> (first except on IOError). >> >> Next, we've gotta look for the entry (which will throw a >> ValueError/IndexError depending on where we fail - if we fail). >> >> Lastly, we need to remove the line and need to check that the write took >> (second IOError exception). >> >> Am I missing how this could be streamlined though? > Well this went with the comment much further down about parsing the files in > general. The idea is that if you got rid of all of the code it would be > simpler. However from your response below it appears that having this is good > so I'll shut up. ;-) > >> >>> line 455-456: Boy that looks unmaintainable. If there is a more >>> maintainable way to do this I >>> would suggest it. If not, you need a big old comment there. >> >> Thanks to Ethan's code review comments this was changed. Please see 457-458 >> from >> http://cr.opensolaris.org/~clayb/delete_service/webrev3.ethan/usr/src/cmd/installadm/delete_service.py.html >> >> >> However, this was just a simple regex looking for: >> "<anything>-s <anything up to a space, stored as path><anything to EOL>" >> and replaced that whole string with what was found and stored as path. > The point was that a lot of people aren't that familiar with reg ex's. Very > prone to maintenance mistakes so explaining what it's doing > and maybe even how would be nice. >> >>> lines 556-559: Seems a little long for list comprehension. I believe we >>> had discussed anything past about >>> 2 lines should be considered for breaking up. Could this be done? It would >>> make the code easier to read >>> and probably easier to maintain. >> >> Sure, this was two lines but I was asked to expand fn to filename by >> another reviewer. How does it work if just pulled out of the for loop. It >> is three lines still but we get a much cleaner presentation (as it's only >> three statements in three lines): >> >> # build a list of grub menus from baseDir >> menus = [filename for filename in os.listdir(baseDir) if >> # only select files which start with grub menu prefix >> filename.startswith(grub_menu_prefix) and >> # do not select the menu file the service uses >> filename != os.path.basename(menuLst.file_name)] > OK. >> >>> line 584: Realistically, could this be in any other field than mountpoint? >>> If not, could this code be simplified? >> >> I'm not sure what code there is to simplify. Line 584 is a comment and the >> next line of code seems trivial to the extreme (just seeing if a string is >> in a list): >> >> (line 587) >> if boot_archive in com.MNTTab().fields.MOUNT_POINT: > Same response as above wrt to mountpoint in /etc/vfstab >> >>> line 612: why? It's not already able to be executed? Are we fixing >>> something here that should be fixed elsewhere? >> >> That's the problem is that it's not executable, we're setting it so that we >> can run it. To set the <tftpdir>/rm.<service name> script so we can run it, >> we have to chmod it. > OK. Seems like something would be wrong it it wasn't executable but it's nice > you're fixing the situation since you can. Why abort if you can fix as you're > doing. >> >> Yes, create-service should get rid of the rm.<service name> scripts. See >> bug: 10853 (create-service:Should consider removing >> /tftpboot/rm.<service|client> scripts). Until then it's an interface were >> someone might stash something so let's honor it until we get rid of them. >> >> As an aside, in the meantime - partially thanks to the 30 create-service >> bugs, the majority of this file is to give us a working foothold from which >> to clean things up. See Drew's comments about how bizarre it is to have a >> script which reverse engineers the entirety of an AI service. >> >>> lines 625-634: Since the point of this function is to remove the tftpboot >>> files, why don't you just remove these files >>> if the rmCmd hasn't? It certainly would be more efficient that appending >>> them to files now and removing them later. >>> And clearer as to the state of the system after this function is called. >>> Plus, the header comments say the function removes them. >> >> Ah thanks I've fixed the comment. The intention is to not remove anything >> except in the main function body, as this aids in debugging since it >> provides a central list and order, as well as allowing breadcrumbs until >> we've gathered all we can since we're piecing together what's left of a >> service. > I'd have to disagree. I'd love to see things removed in the appropriate > functions. So then you would know if you return from function > removeTFTPBootFiles that all the tftpboot files are gone. I'd be confused it > they weren't. >> >> The comment now reads: >> Handle file removal in /tftpboot by building a list of files to remove: >> First, adds pxegrub.<directory pointed to by /tftpboot/<service >> name> i.e. pxegrub.I86PC.OpenSolaris-1 >> Unmounts directory which is boot archive (will be something like >> I86PC.OpenSolaris-4) and removes /etc/vfstab entry for mount point >> >> Calls /tftpboot/rm.<service name> which should remove: >> /tftpboot/<service name> >> /tftpboot/menu.lst.<service name> >> (if the above aren't removed by the rm.<service name> script they >> are added to the remove list) >> Adds /tftpboot/rm.<service name> >> >> Returns: If unable to find tftp root - None >> Success - A list of file paths to remove >> >>> Why not have check_wanboot_conf also make sure everything is cleaned up. >>> Then you don't need the code at 696 ad 699 to >>> add onto the fileToRemove list. Could we also then get rid of 714+? I >>> think all that would be left is then explicit files which >>> could then just be rm'd. In general this seems more complicated than it >>> needs to be. >> >> We need to remove the files provided by the list filesToRemove which has >> the paths returned by: >> find_service_directory >> find_image_path > Can they be removed in the functions? >> >> As well as (for services): >> check_wanboot_conf (for SPARC) >> - or - >> removeTFTPBootFiles (X86) >> > But if these actually removed the files then you wouldn't need them to be > gone here. >> So 714+ gets rid of these collections, not just check_wanboot_conf or >> removeTFTPBootFiles. Further, 696 and 699 are not the same; one is for >> SPARC and thus check_wanboot_conf, while the other is for X86 and thus >> removeTFTPBootFiles. >> >> I'm perhaps missing something in your suggestion here? > I think so. Right now you're checking to see if you need to remove a file and > placing it in a list. The list is later parsed and the file removed. That's > extra computation and complexity. Why not check to see if you need to remove > the file and remove it? Unless the file is needed later it's much simpler. >> >>> usr/src/cmd/installadm/installadm_common.py >>> >>> I lied. I say Ethan's comment about Unix. I agree. >> >> I've changed UnixFile() to File_() >> >>> lines 393-494: do we really need to do this in this manner? For example >>> VFSTab creates a database of the vfstab file. So far I've seen the >>> mountpoint field looked at for the boot_archive. Seems like overkill for >>> that. If we foresee needed more detailed parsability in the future I'm >>> fine with it. Otherwise I wonder.... >> >> Yup, we only use a few fields from each file but this is pretty simple now. >> Many people felt this functionality would be good to have available for >> general extensibility (and as the inetd.conf was a nit request which took >> ten minutes to add (the time for me to man -s 4 inetd.conf and type) it >> seems pretty trivial now (one just needs to give field names and a file >> name). >> >> I'm not sure what you're asking here, other than is this easy? Yes > The greater extensibility answer was appropriate for my question. Guess I > wasn't clear. Sorry. It was a long code review. > > Jean > >> >>> Jean >>> >>> >>> >>> >>> > >