[Libguestfs] Proposed changes for OpenStack

Matthew Booth mbooth at redhat.com
Thu Dec 15 11:19:10 UTC 2011


On 12/15/2011 08:35 AM, Richard W.M. Jones wrote:
> On Wed, Dec 14, 2011 at 05:07:04PM +0000, Matthew Booth wrote:
>> guestmount_mount(g.sock_path(), '/tmp/dir')
>> ...filesystem stuff...
>> guestmount_umount('/tmp/dir')
>>
>> g.close()
>>
>> Note that there are no callbacks, no threads and no races. The code
>> can be written sequentially.
>
> There are certainly threads here, although they would have to be
> hidden behind the scenes, with a barrier to provide synchronization
> before "filesystem stuff" can run.

No, there are no threads and (in this example, anyway) no 
synchronization is required. There *is* a fork, though. 
guestmount_mount() would:

1. Create a new guestfs connection to the existing appliance.
2. Create a new filesystem using the new connection.
3. Mount it.
4. fork() and run the FUSE event loop from the child.

In this case, guestmount_mount() would effectively return after 3, at 
which point the filesystem is already safe to use. Although the above 
example doesn't require this, it would also be 'safe' to interleave 
filesystem access and guestfs commands. The limitations would be that 
changing the appliance mounts while it is guestmounted would have 
undesirable consequences and, as with all filesystem access, there would 
be the potential for read->modify->write races. However, a simple use 
case like the one you proposed won't hit these limitations. If required, 
a simple locking scheme could be added later. This could be as simple as 
total exclusivity for a single client until released.

In working out what guestmount_mount would do, it occurs to me that you 
can do this without requiring either multiple connections to the 
appliance or a callback mechanism, as long as the caller takes 
responsibility for the guestfs handle. Consider 
guestfsmount_mount_handle(g, mountpoint), which does:

1. Create a new filesystem using the given handle.
2. Mount it.
3. Create a new thread to run the FUSE event loop.

You could then do:

g.foo()
h = guestmount_mount_handle(g, mp) (h is an opaque handle)

... it is the responsibility of the caller not to use g here ...

guestmount_unmount(mh)
g.bar()

h would contain at least the mountpoint and the thread handle. 
guestmount_unmount(mh) would therefore be:

umount(mh->mp)
mh->thread.join()

>> Also, we're not bringing FUSE into the main API.
>
> Is this such a problem?  We could dlopen libfuse if the dependency on
> libfuse was a problem ...

Actually, I may be softening to this if the API can be more like a 
regular API call. I think my guestmount_mount_handle example could be 
turned into something usable:

g.foo()
g.mount_local(mp)
... g cannot be used here ...
g.unmount_local()
g.bar()

The handle would be stored in g itself, so only 1 mount would be 
supported at any one time.

This could be usefully extended in 2 ways I can think of:

Firstly, guestfs could be made threadsafe by simply putting a mutex 
around all api calls. That is, calls can be made simultaneously from 2 
different threads, but they will run sequentially. This could be 
improved upon by redesigning the protocol, but I suspect it would be 
perfectly adequate in practice. This would make g usable between calls 
to mount_local and unmount_local(), as long as mount points are not 
modified and the handle is not closed.

Secondly, the mechanism I described before for connecting to a running 
appliance remains generally useful. guestmount could still be updated to 
create a handle which uses an existing appliance.

On the face of it, neither of these extensions seems particularly 
onerous to implement either.

Matt
-- 
Matthew Booth, RHCA, RHCSS
Red Hat Engineering, Virtualisation Team

GPG ID:  D33C3490
GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490




More information about the Libguestfs mailing list