May 6, 2009

Become Friends with find

A while back I noticed a tip posted somewhere on how to use the find utility to register a bunch a virtual machines at once. It was a really helpful post and illustrated some of the potential of the the Swiss Army-like find utility. But it overlooked one of the coolest features of find, the -exec option.

Using -exec, you can launch a command with find and pass each found file as a parameter to it, eliminating the need to run vmware-cmd in a for loop. Just place the command to run after the -exec parameter, and find will replace the string {} with the current file being processed:

[root@esx02 root]# find /vmfs/volumes/ -name '*.vmx' -exec vmware-cmd -s register {} \;
 
register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-dc1/lab-dc1.vmx) = 1
register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-ex1/lab-ex1.vmx) = 1
register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/uda14-esx/uda14-esx.vmx) = 1

The -exec option is often considered less efficient than using xargs, which will feed multiple parameters to a command at once rather than launching the command with a single argument like -exec does. But for use with a command like vmware-cmd, which only expects to have one .vmx file passed to it, -exec is perfect.

And find has another option, -ok, which does the same thing but will present a prompt before running the command on each .vmx file that is found. This can be really handy if you are registering or powering on a bunch of VMs but know that there are some you are going to want to skip:

[root@esx02 root]# find /vmfs/volumes/ -name '*.vmx' -ok vmware-cmd -s register {} \;

< vmware-cmd ... /vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-dc1/lab-dc1.vmx > ? y
register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-dc1/lab-dc1.vmx) = 1

< vmware-cmd ... /vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-ex1/lab-ex1.vmx > ? y
register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-ex1/lab-ex1.vmx) = 1

< vmware-cmd ... /vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/uda14-esx/uda14-esx.vmx > ? n

find rules
I've been in a couple of jams where find really saved the day. Consider a situation where a standalone ESX server needs to be rebuilt, but the virtual machines are all on networked storage. Using find afterwards to register and start the VMs makes the process trivial:

[root@esx02 root]# find /vmfs/volumes/ -name 'lab*.vmx' -exec vmware-cmd -s register {} \;

register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-dc1/lab-dc1.vmx) = 1
register(/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-ex1/lab-ex1.vmx) = 1


[root@esx02 root]# find /vmfs/volumes/ -name 'lab*.vmx' -exec vmware-cmd {} start \;

VMControl error -16: Virtual machine requires user input to continue
VMControl error -16: Virtual machine requires user input to continue


[root@esx02 root]# find /vmfs/volumes/ -name 'lab*.vmx' -print -exec vmware-cmd {} getstate \;

/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-dc1/lab-dc1.vmx
getstate() = stuck

/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-ex1/lab-ex1.vmx
getstate() = stuck


[root@esx02 root]# find /vmfs/volumes/ -name 'lab*.vmx' -print -exec vmware-cmd {} answer \;

/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-dc1/lab-dc1.vmx

Question (id = 1) :msg.uuid.moved:The location of this virtual machine's configuration file has changed since it was last powered on.

If the virtual machine has been copied, you should create a new unique identifier (UUID).  If it has been moved, you should keep its old identifier.

If you are not sure, create a new identifier.

What do you want to do?
        0) Create
        1) Keep
        2) Always Create
        3) Always Keep
        4) Cancel
Select choice. Press enter for default ɘ> : 1
selected 1 : Keep

/vmfs/volumes/495e44a0-d41258bc-fac4-000c299206d0/lab-ex1/lab-ex1.vmx

Question (id = 1) :msg.uuid.moved:The location of this virtual machine's configuration file has changed since it was last powered on.

If the virtual machine has been copied, you should create a new unique identifier (UUID).  If it has been moved, you should keep its old identifier.

If you are not sure, create a new identifier.

What do you want to do?
        0) Create
        1) Keep
        2) Always Create
        3) Always Keep
        4) Cancel
Select choice. Press enter for default ɘ> : 1
selected 1 : Keep

You could even use the -ok option to introduce a pause between the VM start-ups.

Shrimp tacos
The syntax for -exec and -ok can be a little difficult to remember. The ; at the end of the command designates the end of the command find should execute, and it has to be escaped like \; so the shell doesn't interpret it.

I could never get the syntax right from memory until I associated it with shrimp tacos. The curly braces {} are a corn tortilla, and I'm using a spatula to get my shrimp off the skillet, \;

It may be corny, but I never forget the syntax now!

2 comments:

  1. You've got some excellent article Robert! I don't know if you noticed it or not but I've added you to the top 5 blog articles blog on the VMTN blog a couple of times already.

    Anyway, keep up the great work... really enjoy reading this stuff!

    Duncan
    Yellow-Bricks.com
    VMTN Blog

    ReplyDelete
  2. Duncan, thanks for the feedback! I noticed the feed had gotten picked up, but I hadn't seen the top 5 yet, so thanks for letting me know.
    Putting this stuff together has turned out to be way more work than I imagined, how you have time to put Yellow Bricks together in addition to all the other things you do, I'll never understand.
    It's pretty reaffirming to hear you are enjoying the posts, so thanks for all the support!

    ReplyDelete