]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Added docs on extending public API (Dave Allen)
authorDaniel P. Berrange <berrange@redhat.com>
Tue, 9 Jun 2009 11:42:41 +0000 (11:42 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Tue, 9 Jun 2009 11:42:41 +0000 (11:42 +0000)
50 files changed:
ChangeLog
docs/Makefile.am
docs/api.html
docs/api_extension.html [new file with mode: 0644]
docs/api_extension.html.in [new file with mode: 0644]
docs/api_extension/0001-Step-1-of-8-Define-the-public-API.patch [new file with mode: 0644]
docs/api_extension/0002-Step-2-of-8-Define-the-internal-driver-API.patch [new file with mode: 0644]
docs/api_extension/0003-Step-3-of-8-Implement-the-public-API.patch [new file with mode: 0644]
docs/api_extension/0004-Step-4-of-8-Define-the-wire-protocol-format.patch [new file with mode: 0644]
docs/api_extension/0005-Step-5-of-8-Implement-the-RPC-client.patch [new file with mode: 0644]
docs/api_extension/0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch [new file with mode: 0644]
docs/api_extension/0007-Step-7-of-8-Implement-the-driver-methods.patch [new file with mode: 0644]
docs/api_extension/0008-Step-8-of-8-Add-virsh-support.patch [new file with mode: 0644]
docs/archdomain.html
docs/archnetwork.html
docs/archnode.html
docs/archstorage.html
docs/auth.html
docs/bindings.html
docs/deployment.html
docs/docs.html
docs/drivers.html
docs/drvlxc.html
docs/drvopenvz.html
docs/drvqemu.html
docs/drvremote.html
docs/drvtest.html
docs/drvuml.html
docs/drvvbox.html
docs/drvxen.html
docs/format.html
docs/formatcaps.html
docs/formatdomain.html
docs/formatnetwork.html
docs/formatnode.html
docs/formatstorage.html
docs/goals.html
docs/hvsupport.html
docs/internals.html [new file with mode: 0644]
docs/internals.html.in [new file with mode: 0644]
docs/intro.html
docs/java.html
docs/logging.html
docs/python.html
docs/remote.html
docs/sitemap.html
docs/sitemap.html.in
docs/storage.html
docs/uri.html
docs/windows.html

index f50523f89f92138269edb9021a45ee04b94245fe..8621f9e05309361cf705a7f9c0a9944dbd1f1f05 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Tue Jun  9 12:39:24 BST 2009 Daniel P. Berrange <berrange@redhat.com>
+
+       API extension docs (Dave Allen)
+       * docs/internals.html.in, docs/api_extension.html.in,
+       docs/sitemap.html.in: Start new section on libvirt internal
+       development & add doc about public API extensions
+       * docs/api_extension/*patch: Example patch files
+       * docs/*.html: Re-generate for updated sitemap
+
 Thu Jun  4 15:54:24 CEST 2009 Daniel Veillard <veillard@redhat.com>
 
        * src/xm_internals.c: the memory shrinking on device removal
index 97b445686dd4d1b544b867995b6b47af58de5e98..43d04c3c9ecb6f353d73a38f89dbfb4c7f4d7dcb 100644 (file)
@@ -46,6 +46,8 @@ gif = \
 dot_html_in = $(wildcard *.html.in)
 dot_html = $(dot_html_in:%.html.in=%.html)
 
+patches = $(wildcard api_extension/*.patch)
+
 xml = \
   libvirt-api.xml \
   libvirt-refs.xml \
@@ -67,6 +69,7 @@ EXTRA_DIST=                                   \
   site.xsl newapi.xsl news.xsl page.xsl        ChangeLog.xsl   \
   $(dot_html) $(dot_html_in) $(gif) $(apihtml) $(apipng) \
   $(xml) $(fig) $(png) \
+  $(patches) \
   virsh.pod ChangeLog.awk
 
 all: web $(top_builddir)/NEWS $(man_MANS)
index e799e5161882a8966060c57261d0177eec849467..a1f3855567e60477c410924727edf2b9eb84995a 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
diff --git a/docs/api_extension.html b/docs/api_extension.html
new file mode 100644 (file)
index 0000000..933e5fd
--- /dev/null
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+        This file is autogenerated from api_extension.html.in
+        Do not edit this file. Changes will be lost.
+      -->
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+    <link rel="stylesheet" type="text/css" href="main.css" />
+    <link rel="SHORTCUT ICON" href="32favicon.png" />
+    <title>libvirt: Implementing a new API in Libvirt</title>
+    <meta name="description" content="libvirt, virtualization, virtualization API" />
+  </head>
+  <body>
+    <div id="header">
+      <div id="headerLogo"></div>
+      <div id="headerSearch">
+        <form action="search.php" enctype="application/x-www-form-urlencoded" method="get"><div>
+            <input id="query" name="query" type="text" size="12" value="" />
+            <input id="submit" name="submit" type="submit" value="Search" />
+          </div></form>
+      </div>
+    </div>
+    <div id="body">
+      <div id="menu">
+        <ul class="l0"><li>
+            <div>
+              <a title="Front page of the libvirt website" class="inactive" href="index.html">Home</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Details of new features and bugs fixed in each release" class="inactive" href="news.html">News</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Get the latest source releases, binary builds and get access to the source repository" class="inactive" href="downloads.html">Downloads</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Information for users, administrators and developers" class="active" href="docs.html">Documentation</a>
+              <ul class="l1"><li>
+                  <div>
+                    <a title="Information about deploying and using libvirt" class="inactive" href="deployment.html">Deployment</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Overview of the logical subsystems in the libvirt API" class="inactive" href="intro.html">Architecture</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Description of the XML formats used in libvirt" class="inactive" href="format.html">XML format</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Hypervisor specific driver information" class="inactive" href="drivers.html">Drivers</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Reference manual for the C public API" class="inactive" href="html/index.html">API reference</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="active" href="internals.html">Internals</a>
+                    <ul class="l2"><li>
+                        <div>
+                          <span class="active">API extensions</span>
+                        </div>
+                      </li></ul>
+                  </div>
+                </li></ul>
+            </div>
+          </li><li>
+            <div>
+              <a title="User contributed content" class="inactive" href="http://wiki.libvirt.org">Wiki</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Frequently asked questions" class="inactive" href="FAQ.html">FAQ</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="How and where to report bugs and request features" class="inactive" href="bugs.html">Bug reports</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="How to contact the developers via email and IRC" class="inactive" href="contact.html">Contact</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Miscellaneous links of interest related to libvirt" class="inactive" href="relatedlinks.html">Related Links</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Overview of all content on the website" class="inactive" href="sitemap.html">Sitemap</a>
+            </div>
+          </li></ul>
+      </div>
+      <div id="content">
+        <h1>Implementing a new API in Libvirt</h1>
+        <ul><li>
+            <a href="#publicapi">Defining the public API</a>
+          </li><li>
+            <a href="#internalapi">Defining the internal API</a>
+          </li><li>
+            <a href="#implpublic">Implementing the public API</a>
+          </li><li>
+            <a href="#wireproto">Defining the wire protocol format</a>
+          </li><li>
+            <a href="#rpcclient">Implement the RPC client</a>
+          </li><li>
+            <a href="#serverdispatch">Implement the server side dispatcher</a>
+          </li><li>
+            <a href="#driverimpl">Implement the driver methods</a>
+          </li><li>
+            <a href="#virsh">Implement virsh commands</a>
+          </li></ul>
+        <p>
+      This document walks you through the process of implementing a new
+      API in libvirt.  It uses as an example the addition of the node device
+      create and destroy APIs.
+    </p>
+        <p>
+      Before you begin coding, it is critical that you propose your
+      changes on the libvirt mailing list and get feedback on your ideas to
+      make sure what you're proposing fits with the general direction of the
+      project.  Even before doing a proof of concept implementation, send an
+      email giving an overview of the functionality you think should be
+      added to libvirt.  Someone may already be working on the feature you
+      want.  Also, recognize that everything you write is likely to undergo
+      significant rework as you discuss it with the other developers, so
+      don't wait too long before getting feedback.
+    </p>
+        <p>
+      Adding a new API to libvirt is not difficult, but there are quite a
+      few steps.  This document assumes that you are familiar with C
+      programming and have checked out the libvirt code from the source code
+      repository and successfully built the existing tree.  Instructions on
+      how to check out and build the code can be found at:
+    </p>
+        <p>
+      <a href="http://libvirt.org/downloads.html">http://libvirt.org/downloads.html</a>
+    </p>
+        <p>
+      Once you have a working development environment, the steps to create a
+      new API are:
+    </p>
+        <ol><li>define the public API</li><li>define the internal driver API</li><li>implement the public API</li><li>define the wire protocol format</li><li>implement the RPC client</li><li>implement the server side dispatcher</li><li>implement the driver methods</li><li>add virsh support</li></ol>
+        <p>
+      It is, of course, possible to implement the pieces in any order, but
+      if the development tasks are completed in the order listed, the code
+      will compile after each step.  Given the number of changes required,
+      verification after each step is highly recommended.
+    </p>
+        <p>
+      Submit new code in the form shown in the example code: one patch
+      per step.  That's not to say submit patches before you have working
+      functionality--get the whole thing working and make sure you're happy
+      with it.  Then use git or some other version control system that lets
+      you rewrite your commit history and break patches into pieces so you
+      don't drop a big blob of code on the mailing list at one go.  For
+      example, I didn't follow my own advice when I originally submitted the
+      example code to the libvirt list but rather submitted it in several
+      large chunks.  I've used git's ability to rewrite my commit history to
+      break the code apart into the example patches shown.
+    </p>
+        <p>
+      Don't mix anything else into the patches you submit.  The patches
+      should be the minimal changes required to implement the functionality
+      you're adding.  If you notice a bug in unrelated code (i.e., code you
+      don't have to touch to implement your API change) during development,
+      create a patch that just addresses that bug and submit it
+      separately.
+    </p>
+        <p>With that said, let's begin.</p>
+        <h2>
+          <a name="publicapi" id="publicapi">Defining the public API</a>
+        </h2>
+        <p>The first task is to define the public API and add it to:</p>
+        <p>
+          <code>include/libvirt/libvirt.h.in</code>
+        </p>
+        <p>
+      This task is in many ways the most important to get right, since once
+      the API has been committed to the repository, it's libvirt's policy
+      never to change it.  Mistakes in the implementation are bugs that you
+      can fix.  Make a mistake in the API definition and you're stuck with
+      it, so think carefully about the interface and don't be afraid to
+      rework it as you go through the process of implementing it.
+    </p>
+        <p>Once you have defined the API, you have to add the symbol names to:</p>
+        <p>
+          <code>src/libvirt_public.syms</code>
+        </p>
+        <p class="example">See <a href="api_extension/0001-Step-1-of-8-Define-the-public-API.patch">0001-Step-1-of-8-Define-the-public-API.patch</a> for example code.</p>
+        <h2>
+          <a name="internalapi" id="internalapi">Defining the internal API</a>
+        </h2>
+        <p>
+      Each public API call is associated with a driver, such as a host
+      virtualization driver, a network virtualization driver, a storage
+      virtualization driver, a state driver, or a device monitor.  Adding
+      the internal API is ordinarily a matter of adding a new member to the
+      struct representing one of these drivers.
+    </p>
+        <p>
+      Of course, it's possible that the new API will involve the creation of
+      an entire new driver type, in which case the changes will include the
+      creation of a new struct type to represent the new driver type.
+    </p>
+        <p>The driver structs are defined in:</p>
+        <p>
+          <code>src/driver.h</code>
+        </p>
+        <p>
+      To define the internal API, first typedef the driver function
+      prototype and then add a new field for it to the relevant driver
+      struct.
+    </p>
+        <p class="example">See <a href="api_extension/0002-Step-2-of-8-Define-the-internal-driver-API.patch">0002-Step-2-of-8-Define-the-internal-driver-API.patch</a></p>
+        <h2>
+          <a name="implpublic" id="implpublic">Implementing the public API</a>
+        </h2>
+        <p>
+      Implementing the public API is largely a formality in which we wire up
+      public API to the internal driver API.  The public API implementation
+      takes care of some basic validity checks before passing control to the
+      driver implementation.  In RFC 2119 vocabulary, this function:
+    </p>
+        <ol class="ordinarylist"><li>SHOULD log a message with VIR_DEBUG() indicating that it is
+       being called and its parameters;</li><li>MUST call virResetLastError();</li><li>SHOULD confirm that the connection is valid with
+       VIR_IS_CONNECT(conn);</li><li><strong>SECURITY: If the API requires a connection with write
+         privileges, MUST confirm that the connection flags do not
+         indicate that the connection is read-only;</strong></li><li>SHOULD do basic validation of the parameters that are being
+       passed in;</li><li>MUST confirm that the driver for this connection exists and that
+       it implements this function;</li><li>MUST call the internal API;</li><li>SHOULD log a message with VIR_DEBUG() indicating that it is
+       returning, its return value, and status.</li><li>MUST return status to the caller.</li></ol>
+        <p>The public API calls are implemented in:</p>
+        <p>
+          <code>src/libvirt.c</code>
+        </p>
+        <p class="example">See <a href="api_extension/0003-Step-3-of-8-Implement-the-public-API.patch">0003-Step-3-of-8-Implement-the-public-API.patch</a></p>
+        <h2>
+          <a name="wireproto" id="wireproto">Defining the wire protocol format</a>
+        </h2>
+        <p>
+      Defining the wire protocol is essentially a straightforward exercise
+      which is probably most easily understood by referring to the existing
+      remote protocol wire format definitions and the example patch.  It
+      involves making two additions to:
+    </p>
+        <p>
+          <code>qemud/remote_protocol.x</code>
+        </p>
+        <p>
+      First, create two new structs for each new function that you're adding
+      to the API.  One struct describes the parameters to be passed to the
+      remote function, and a second struct describes the value returned by
+      the remote function.  The one exception to this rule is that functions
+      that return only integer status do not require a struct for returned
+      data.
+    </p>
+        <p>
+      Second, add values to the remote_procedure enum for each new function
+      added to the API.
+    </p>
+        <p class="example">See <a href="api_extension/0004-Step-4-of-8-Define-the-wire-protocol-format.patch">0004-Step-4-of-8-Define-the-wire-protocol-format.patch</a></p>
+        <p>
+      Once these changes are in place, it's necessary to run 'make rpcgen'
+      in the qemud directory to create the .c and .h files required by the
+      remote protocol code. This must be done on a Linux host using the
+      GLibC rpcgen program. Other rpcgen versions may generate code which
+      results in bogus compile time warnings
+    </p>
+        <h2>
+          <a name="rpcclient" id="rpcclient">Implement the RPC client</a>
+        </h2>
+        <p>
+      Implementing the RPC client is also relatively mechanical, so refer to
+      the exising code and example patch for guidance.  The RPC client uses
+      the rpcgen generated .h files.  The remote method calls go in:
+    </p>
+        <p>
+          <code>src/remote_internal.c</code>
+        </p>
+        <p>Each remote method invocation does the following:</p>
+        <ol class="ordinarylist"><li>locks the remote driver;</li><li>sets up the method arguments;</li><li>invokes the remote function;</li><li>checks the return value, if necessary;</li><li>extracts any returned data;</li><li>frees any returned data;</li><li>unlocks the remote driver.</li></ol>
+        <p>
+      Once you have created the remote method calls, you have to add fields
+      for them to the driver structs for the appropriate remote driver.
+    </p>
+        <p class="example">See <a href="api_extension/0005-Step-5-of-8-Implement-the-RPC-client.patch">0005-Step-5-of-8-Implement-the-RPC-client.patch</a></p>
+        <h2>
+          <a name="serverdispatch" id="serverdispatch">Implement the server side dispatcher</a>
+        </h2>
+        <p>
+      Implementing the server side of the remote function calls is simply a
+      matter of deserializing the parameters passed in from the remote
+      caller and passing them to the corresponding internal API function.
+      The server side dispatchers are implemented in:
+    </p>
+        <p>
+          <code>qemud/remote.c</code>
+        </p>
+        <p>Again, this step uses the .h files generated by make rpcgen.</p>
+        <p class="example">See <a href="api_extension/0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch">0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch</a></p>
+        <h2>
+          <a name="driverimpl" id="driverimpl">Implement the driver methods</a>
+        </h2>
+        <p>
+      So, after all that, we get to the fun part.  All functionality in
+      libvirt is implemented inside a driver.  Thus, here is where you
+      implement whatever functionality you're adding to libvirt.  You'll
+      either need to add additional files to the src directory or extend
+      files that are already there, depending on what functionality you're
+      adding.
+    </p>
+        <p>
+      In the example code, the extension is only an additional two function
+      calls in the node device API, so most of the new code is additions to
+      existing files.  The only new files are there for multi-platform
+      implementation convenience, as some of the new code is Linux specific.
+    </p>
+        <p>
+      The example code is probably uninteresting unless you're concerned
+      with libvirt storage, but I've included it here to show how new files
+      are added to the build environment.
+    </p>
+        <p class="example">See <a href="api_extension/0007-Step-7-of-8-Implement-the-driver-methods.patch">0007-Step-7-of-8-Implement-the-driver-methods.patch</a></p>
+        <h2>
+          <a name="virsh" id="virsh">Implement virsh commands</a>
+        </h2>
+        <p>
+      Once you have the new functionality in place, the easiest way to test
+      it and also to provide it to end users is to implement support for it
+      in virsh.
+    </p>
+        <p>
+      A virsh command is composed of a few pieces of code.  You need to
+      define an array of vshCmdInfo structs for each new command that
+      contain the help text and the command description text.  You also need
+      an array of vshCmdOptDef structs to describe the command options.
+      Once you have those pieces of data in place you can write the function
+      implementing the virsh command.  Finally, you need to add the new
+      command to the commands[] array.
+    </p>
+        <p class="example">See <a href="api_extension/0008-Step-8-of-8-Add-virsh-support.patch">0008-Step-8-of-8-Add-virsh-support.patch</a></p>
+        <p>Once you have working functionality, run make check and make
+      syntax-check before generating patches.</p>
+      </div>
+    </div>
+    <div id="footer">
+      <p id="sponsor">
+           Sponsored by:<br /><a href="http://et.redhat.com/"><img src="et.png" alt="Project sponsored by Red Hat Emerging Technology" /></a></p>
+    </div>
+  </body>
+</html>
diff --git a/docs/api_extension.html.in b/docs/api_extension.html.in
new file mode 100644 (file)
index 0000000..2cbd2bd
--- /dev/null
@@ -0,0 +1,302 @@
+<html>
+  <head>
+    <title>Implementing a new API in Libvirt</title>
+  </head>
+
+  <body>
+    <h1>Implementing a new API in Libvirt</h1>
+
+    <ul id="toc"></ul>
+
+    <p>
+      This document walks you through the process of implementing a new
+      API in libvirt.  It uses as an example the addition of the node device
+      create and destroy APIs.
+    </p>
+
+    <p>
+      Before you begin coding, it is critical that you propose your
+      changes on the libvirt mailing list and get feedback on your ideas to
+      make sure what you're proposing fits with the general direction of the
+      project.  Even before doing a proof of concept implementation, send an
+      email giving an overview of the functionality you think should be
+      added to libvirt.  Someone may already be working on the feature you
+      want.  Also, recognize that everything you write is likely to undergo
+      significant rework as you discuss it with the other developers, so
+      don't wait too long before getting feedback.
+    </p>
+
+    <p>
+      Adding a new API to libvirt is not difficult, but there are quite a
+      few steps.  This document assumes that you are familiar with C
+      programming and have checked out the libvirt code from the source code
+      repository and successfully built the existing tree.  Instructions on
+      how to check out and build the code can be found at:
+    </p>
+
+    <p>
+      <a href="http://libvirt.org/downloads.html">http://libvirt.org/downloads.html</a>
+    </p>
+
+    <p>
+      Once you have a working development environment, the steps to create a
+      new API are:
+    </p>
+    <ol>
+      <li>define the public API</li>
+      <li>define the internal driver API</li>
+      <li>implement the public API</li>
+      <li>define the wire protocol format</li>
+      <li>implement the RPC client</li>
+      <li>implement the server side dispatcher</li>
+      <li>implement the driver methods</li>
+      <li>add virsh support</li>
+    </ol>
+
+    <p>
+      It is, of course, possible to implement the pieces in any order, but
+      if the development tasks are completed in the order listed, the code
+      will compile after each step.  Given the number of changes required,
+      verification after each step is highly recommended.
+    </p>
+
+    <p>
+      Submit new code in the form shown in the example code: one patch
+      per step.  That's not to say submit patches before you have working
+      functionality--get the whole thing working and make sure you're happy
+      with it.  Then use git or some other version control system that lets
+      you rewrite your commit history and break patches into pieces so you
+      don't drop a big blob of code on the mailing list at one go.  For
+      example, I didn't follow my own advice when I originally submitted the
+      example code to the libvirt list but rather submitted it in several
+      large chunks.  I've used git's ability to rewrite my commit history to
+      break the code apart into the example patches shown.
+    </p>
+
+    <p>
+      Don't mix anything else into the patches you submit.  The patches
+      should be the minimal changes required to implement the functionality
+      you're adding.  If you notice a bug in unrelated code (i.e., code you
+      don't have to touch to implement your API change) during development,
+      create a patch that just addresses that bug and submit it
+      separately.
+    </p>
+
+    <p>With that said, let's begin.</p>
+
+    <h2><a name='publicapi'>Defining the public API</a></h2>
+
+    <p>The first task is to define the public API and add it to:</p>
+
+    <p><code>include/libvirt/libvirt.h.in</code></p>
+
+    <p>
+      This task is in many ways the most important to get right, since once
+      the API has been committed to the repository, it's libvirt's policy
+      never to change it.  Mistakes in the implementation are bugs that you
+      can fix.  Make a mistake in the API definition and you're stuck with
+      it, so think carefully about the interface and don't be afraid to
+      rework it as you go through the process of implementing it.
+    </p>
+
+    <p>Once you have defined the API, you have to add the symbol names to:</p>
+
+    <p><code>src/libvirt_public.syms</code></p>
+
+    <p class="example">See <a href="api_extension/0001-Step-1-of-8-Define-the-public-API.patch">0001-Step-1-of-8-Define-the-public-API.patch</a> for example code.</p>
+
+
+    <h2><a name='internalapi'>Defining the internal API</a></h2>
+
+    <p>
+      Each public API call is associated with a driver, such as a host
+      virtualization driver, a network virtualization driver, a storage
+      virtualization driver, a state driver, or a device monitor.  Adding
+      the internal API is ordinarily a matter of adding a new member to the
+      struct representing one of these drivers.
+    </p>
+
+    <p>
+      Of course, it's possible that the new API will involve the creation of
+      an entire new driver type, in which case the changes will include the
+      creation of a new struct type to represent the new driver type.
+    </p>
+
+    <p>The driver structs are defined in:</p>
+
+    <p><code>src/driver.h</code></p>
+
+    <p>
+      To define the internal API, first typedef the driver function
+      prototype and then add a new field for it to the relevant driver
+      struct.
+    </p>
+
+    <p class="example">See <a href="api_extension/0002-Step-2-of-8-Define-the-internal-driver-API.patch">0002-Step-2-of-8-Define-the-internal-driver-API.patch</a></p>
+
+    <h2><a name='implpublic'>Implementing the public API</a></h2>
+
+    <p>
+      Implementing the public API is largely a formality in which we wire up
+      public API to the internal driver API.  The public API implementation
+      takes care of some basic validity checks before passing control to the
+      driver implementation.  In RFC 2119 vocabulary, this function:
+    </p>
+
+    <ol class="ordinarylist">
+      <li>SHOULD log a message with VIR_DEBUG() indicating that it is
+       being called and its parameters;</li>
+      <li>MUST call virResetLastError();</li>
+      <li>SHOULD confirm that the connection is valid with
+       VIR_IS_CONNECT(conn);</li>
+      <li><strong>SECURITY: If the API requires a connection with write
+         privileges, MUST confirm that the connection flags do not
+         indicate that the connection is read-only;</strong></li>
+      <li>SHOULD do basic validation of the parameters that are being
+       passed in;</li>
+      <li>MUST confirm that the driver for this connection exists and that
+       it implements this function;</li>
+      <li>MUST call the internal API;</li>
+      <li>SHOULD log a message with VIR_DEBUG() indicating that it is
+       returning, its return value, and status.</li>
+      <li>MUST return status to the caller.</li>
+    </ol>
+
+    <p>The public API calls are implemented in:</p>
+
+    <p><code>src/libvirt.c</code></p>
+
+    <p class="example">See <a href="api_extension/0003-Step-3-of-8-Implement-the-public-API.patch">0003-Step-3-of-8-Implement-the-public-API.patch</a></p>
+
+
+    <h2><a name='wireproto'>Defining the wire protocol format</a></h2>
+
+    <p>
+      Defining the wire protocol is essentially a straightforward exercise
+      which is probably most easily understood by referring to the existing
+      remote protocol wire format definitions and the example patch.  It
+      involves making two additions to:
+    </p>
+
+    <p><code>qemud/remote_protocol.x</code></p>
+
+    <p>
+      First, create two new structs for each new function that you're adding
+      to the API.  One struct describes the parameters to be passed to the
+      remote function, and a second struct describes the value returned by
+      the remote function.  The one exception to this rule is that functions
+      that return only integer status do not require a struct for returned
+      data.
+    </p>
+
+    <p>
+      Second, add values to the remote_procedure enum for each new function
+      added to the API.
+    </p>
+
+    <p class="example">See <a href="api_extension/0004-Step-4-of-8-Define-the-wire-protocol-format.patch">0004-Step-4-of-8-Define-the-wire-protocol-format.patch</a></p>
+
+    <p>
+      Once these changes are in place, it's necessary to run 'make rpcgen'
+      in the qemud directory to create the .c and .h files required by the
+      remote protocol code. This must be done on a Linux host using the
+      GLibC rpcgen program. Other rpcgen versions may generate code which
+      results in bogus compile time warnings
+    </p>
+
+
+    <h2><a name='rpcclient'>Implement the RPC client</a></h2>
+
+    <p>
+      Implementing the RPC client is also relatively mechanical, so refer to
+      the exising code and example patch for guidance.  The RPC client uses
+      the rpcgen generated .h files.  The remote method calls go in:
+    </p>
+
+    <p><code>src/remote_internal.c</code></p>
+
+    <p>Each remote method invocation does the following:</p>
+
+    <ol class="ordinarylist">
+      <li>locks the remote driver;</li>
+      <li>sets up the method arguments;</li>
+      <li>invokes the remote function;</li>
+      <li>checks the return value, if necessary;</li>
+      <li>extracts any returned data;</li>
+      <li>frees any returned data;</li>
+      <li>unlocks the remote driver.</li>
+    </ol>
+
+    <p>
+      Once you have created the remote method calls, you have to add fields
+      for them to the driver structs for the appropriate remote driver.
+    </p>
+
+    <p class="example">See <a href="api_extension/0005-Step-5-of-8-Implement-the-RPC-client.patch">0005-Step-5-of-8-Implement-the-RPC-client.patch</a></p>
+
+    <h2><a name="serverdispatch">Implement the server side dispatcher</a></h2>
+
+    <p>
+      Implementing the server side of the remote function calls is simply a
+      matter of deserializing the parameters passed in from the remote
+      caller and passing them to the corresponding internal API function.
+      The server side dispatchers are implemented in:
+    </p>
+
+    <p><code>qemud/remote.c</code></p>
+
+    <p>Again, this step uses the .h files generated by make rpcgen.</p>
+
+    <p class="example">See <a href="api_extension/0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch">0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch</a></p>
+
+
+    <h2><a name="driverimpl">Implement the driver methods</a></h2>
+
+    <p>
+      So, after all that, we get to the fun part.  All functionality in
+      libvirt is implemented inside a driver.  Thus, here is where you
+      implement whatever functionality you're adding to libvirt.  You'll
+      either need to add additional files to the src directory or extend
+      files that are already there, depending on what functionality you're
+      adding.
+    </p>
+
+    <p>
+      In the example code, the extension is only an additional two function
+      calls in the node device API, so most of the new code is additions to
+      existing files.  The only new files are there for multi-platform
+      implementation convenience, as some of the new code is Linux specific.
+    </p>
+
+    <p>
+      The example code is probably uninteresting unless you're concerned
+      with libvirt storage, but I've included it here to show how new files
+      are added to the build environment.
+    </p>
+
+    <p class="example">See <a href="api_extension/0007-Step-7-of-8-Implement-the-driver-methods.patch">0007-Step-7-of-8-Implement-the-driver-methods.patch</a></p>
+
+    <h2><a name="virsh">Implement virsh commands</a></h2>
+
+    <p>
+      Once you have the new functionality in place, the easiest way to test
+      it and also to provide it to end users is to implement support for it
+      in virsh.
+    </p>
+
+    <p>
+      A virsh command is composed of a few pieces of code.  You need to
+      define an array of vshCmdInfo structs for each new command that
+      contain the help text and the command description text.  You also need
+      an array of vshCmdOptDef structs to describe the command options.
+      Once you have those pieces of data in place you can write the function
+      implementing the virsh command.  Finally, you need to add the new
+      command to the commands[] array.
+    </p>
+
+    <p class="example">See <a href="api_extension/0008-Step-8-of-8-Add-virsh-support.patch">0008-Step-8-of-8-Add-virsh-support.patch</a></p>
+
+    <p>Once you have working functionality, run make check and make
+      syntax-check before generating patches.</p>
+  </body>
+</html>
diff --git a/docs/api_extension/0001-Step-1-of-8-Define-the-public-API.patch b/docs/api_extension/0001-Step-1-of-8-Define-the-public-API.patch
new file mode 100644 (file)
index 0000000..6d0cd68
--- /dev/null
@@ -0,0 +1,45 @@
+From 2ae8fd62a1e5e085b7902da9bc207b806d84fd91 Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:16:11 -0400
+Subject: [PATCH] Step 1 of 8 Define the public API
+
+---
+ include/libvirt/libvirt.h.in |    6 ++++++
+ src/libvirt_public.syms      |    6 ++++++
+ 2 files changed, 12 insertions(+), 0 deletions(-)
+
+diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
+index a028b21..2f7076f 100644
+--- a/include/libvirt/libvirt.h.in
++++ b/include/libvirt/libvirt.h.in
+@@ -1124,6 +1124,12 @@ int                     virNodeDeviceDettach    (virNodeDevicePtr dev);
+ int                     virNodeDeviceReAttach   (virNodeDevicePtr dev);
+ int                     virNodeDeviceReset      (virNodeDevicePtr dev);
++virNodeDevicePtr        virNodeDeviceCreateXML  (virConnectPtr conn,
++                                                 const char *xmlDesc,
++                                                 unsigned int flags);
++
++int                     virNodeDeviceDestroy    (virNodeDevicePtr dev);
++
+ /*
+  * Domain Event Notification
+  */
+diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
+index f7ebbc3..b8f9128 100644
+--- a/src/libvirt_public.syms
++++ b/src/libvirt_public.syms
+@@ -258,4 +258,10 @@ LIBVIRT_0.6.1 {
+       virNodeGetSecurityModel;
+ } LIBVIRT_0.6.0;
++LIBVIRT_0.6.3 {
++    global:
++      virNodeDeviceCreateXML;
++      virNodeDeviceDestroy;
++} LIBVIRT_0.6.1;
++
+ # .... define new API here using predicted next version number ....
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0002-Step-2-of-8-Define-the-internal-driver-API.patch b/docs/api_extension/0002-Step-2-of-8-Define-the-internal-driver-API.patch
new file mode 100644 (file)
index 0000000..231cbdf
--- /dev/null
@@ -0,0 +1,37 @@
+From b26d7fc2d64e7e6e4d3ea2b43361015d3620d7a6 Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:19:14 -0400
+Subject: [PATCH] Step 2 of 8 Define the internal driver API
+
+---
+ src/driver.h |    7 +++++++
+ 1 files changed, 7 insertions(+), 0 deletions(-)
+
+diff --git a/src/driver.h b/src/driver.h
+index 39dc413..c357b76 100644
+--- a/src/driver.h
++++ b/src/driver.h
+@@ -684,6 +684,11 @@ typedef int (*virDevMonDeviceListCaps)(virNodeDevicePtr dev,
+                                        char **const names,
+                                        int maxnames);
++typedef virNodeDevicePtr (*virDrvNodeDeviceCreateXML)(virConnectPtr conn,
++                                                      const char *xmlDesc,
++                                                      unsigned int flags);
++typedef int (*virDrvNodeDeviceDestroy)(virNodeDevicePtr dev);
++
+ /**
+  * _virDeviceMonitor:
+  *
+@@ -702,6 +707,8 @@ struct _virDeviceMonitor {
+     virDevMonDeviceGetParent deviceGetParent;
+     virDevMonDeviceNumOfCaps deviceNumOfCaps;
+     virDevMonDeviceListCaps deviceListCaps;
++    virDrvNodeDeviceCreateXML deviceCreateXML;
++    virDrvNodeDeviceDestroy deviceDestroy;
+ };
+ /*
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0003-Step-3-of-8-Implement-the-public-API.patch b/docs/api_extension/0003-Step-3-of-8-Implement-the-public-API.patch
new file mode 100644 (file)
index 0000000..079bd06
--- /dev/null
@@ -0,0 +1,120 @@
+From fc585594a207dfb9149e7d3d01c9eb1c79b6d52d Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:22:23 -0400
+Subject: [PATCH] Step 3 of 8 Implement the public API
+
+---
+ src/libvirt.c |   97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 97 insertions(+), 0 deletions(-)
+
+diff --git a/src/libvirt.c b/src/libvirt.c
+index f3d4484..ded18a7 100644
+--- a/src/libvirt.c
++++ b/src/libvirt.c
+@@ -7509,6 +7509,103 @@ error:
+ }
++/**
++ * virNodeDeviceCreateXML:
++ * @conn: pointer to the hypervisor connection
++ * @xmlDesc: string containing an XML description of the device to be created
++ * @flags: callers should always pass 0
++ *
++ * Create a new device on the VM host machine, for example, virtual
++ * HBAs created using vport_create.
++ *
++ * Returns a node device object if successful, NULL in case of failure
++ */
++virNodeDevicePtr
++virNodeDeviceCreateXML(virConnectPtr conn,
++                       const char *xmlDesc,
++                       unsigned int flags)
++{
++    VIR_DEBUG("conn=%p, xmlDesc=%s, flags=%d", conn, xmlDesc, flags);
++
++    virResetLastError();
++
++    if (!VIR_IS_CONNECT(conn)) {
++        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
++        return NULL;
++    }
++
++    if (conn->flags & VIR_CONNECT_RO) {
++        virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
++        goto error;
++    }
++
++    if (xmlDesc == NULL) {
++        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
++        goto error;
++    }
++
++    if (conn->deviceMonitor &&
++        conn->deviceMonitor->deviceCreateXML) {
++        virNodeDevicePtr dev = conn->deviceMonitor->deviceCreateXML(conn, xmlDesc, flags);
++        if (dev == NULL)
++            goto error;
++        return dev;
++    }
++
++    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
++
++error:
++    /* Copy to connection error object for back compatability */
++    virSetConnError(conn);
++    return NULL;
++}
++
++
++/**
++ * virNodeDeviceDestroy:
++ * @dev: a device object
++ *
++ * Destroy the device object. The virtual device is removed from the host operating system.
++ * This function may require privileged access
++ *
++ * Returns 0 in case of success and -1 in case of failure.
++ */
++int
++virNodeDeviceDestroy(virNodeDevicePtr dev)
++{
++    DEBUG("dev=%p", dev);
++
++    virResetLastError();
++
++    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
++        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
++        return (-1);
++    }
++
++    if (dev->conn->flags & VIR_CONNECT_RO) {
++        virLibConnError(dev->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
++        goto error;
++    }
++
++    if (dev->conn->deviceMonitor &&
++        dev->conn->deviceMonitor->deviceDestroy) {
++        int retval = dev->conn->deviceMonitor->deviceDestroy(dev);
++        if (retval < 0) {
++            goto error;
++        }
++
++        return 0;
++    }
++
++    virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
++
++error:
++    /* Copy to connection error object for back compatability */
++    virSetConnError(dev->conn);
++    return -1;
++}
++
++
+ /*
+  * Domain Event Notification
+  */
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0004-Step-4-of-8-Define-the-wire-protocol-format.patch b/docs/api_extension/0004-Step-4-of-8-Define-the-wire-protocol-format.patch
new file mode 100644 (file)
index 0000000..8990263
--- /dev/null
@@ -0,0 +1,48 @@
+From bce8f1243b0454c0d70e3db832a039d22faab09a Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Wed, 20 May 2009 13:58:58 -0400
+Subject: [PATCH] Step 4 of 8 Define the wire protocol format
+
+---
+ qemud/remote_protocol.x |   18 +++++++++++++++++-
+ 1 files changed, 17 insertions(+), 1 deletions(-)
+
+diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
+index 2d8e6a2..2c79949 100644
+--- a/qemud/remote_protocol.x
++++ b/qemud/remote_protocol.x
+@@ -1109,6 +1109,19 @@ struct remote_node_device_reset_args {
+     remote_nonnull_string name;
+ };
++struct remote_node_device_create_xml_args {
++    remote_nonnull_string xml_desc;
++    int flags;
++};
++
++struct remote_node_device_create_xml_ret {
++    remote_nonnull_node_device dev;
++};
++
++struct remote_node_device_destroy_args {
++    remote_nonnull_string name;
++};
++
+ /**
+  * Events Register/Deregister:
+@@ -1270,7 +1283,10 @@ enum remote_procedure {
+     REMOTE_PROC_NODE_DEVICE_RESET = 120,
+     REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL = 121,
+-    REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122
++    REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
++
++    REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123,
++    REMOTE_PROC_NODE_DEVICE_DESTROY = 124
+ };
+ /* Custom RPC structure. */
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0005-Step-5-of-8-Implement-the-RPC-client.patch b/docs/api_extension/0005-Step-5-of-8-Implement-the-RPC-client.patch
new file mode 100644 (file)
index 0000000..6f87dea
--- /dev/null
@@ -0,0 +1,85 @@
+From ff272552c297966ace3492aefe91fc830152251a Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:26:12 -0400
+Subject: [PATCH] Step 5 of 8 Implement the RPC client
+
+---
+ src/remote_internal.c |   55 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 55 insertions(+), 0 deletions(-)
+
+diff --git a/src/remote_internal.c b/src/remote_internal.c
+index 4b3afb0..e665ef8 100644
+--- a/src/remote_internal.c
++++ b/src/remote_internal.c
+@@ -4978,6 +4978,59 @@ done:
+ }
++static virNodeDevicePtr
++remoteNodeDeviceCreateXML(virConnectPtr conn,
++                          const char *xmlDesc,
++                          unsigned int flags)
++{
++    remote_node_device_create_xml_args args;
++    remote_node_device_create_xml_ret ret;
++    virNodeDevicePtr dev = NULL;
++    struct private_data *priv = conn->privateData;
++
++    remoteDriverLock(priv);
++
++    memset(&ret, 0, sizeof ret);
++    args.xml_desc = (char *)xmlDesc;
++    args.flags = flags;
++
++    if (call(conn, priv, 0, REMOTE_PROC_NODE_DEVICE_CREATE_XML,
++             (xdrproc_t) xdr_remote_node_device_create_xml_args, (char *) &args,
++             (xdrproc_t) xdr_remote_node_device_create_xml_ret, (char *) &ret) == -1)
++        goto done;
++
++    dev = get_nonnull_node_device(conn, ret.dev);
++    xdr_free ((xdrproc_t) xdr_remote_node_device_create_xml_ret, (char *) &ret);
++
++done:
++    remoteDriverUnlock(priv);
++    return dev;
++}
++
++static int
++remoteNodeDeviceDestroy(virNodeDevicePtr dev)
++{
++    int rv = -1;
++    remote_node_device_destroy_args args;
++    struct private_data *priv = dev->conn->privateData;
++
++    remoteDriverLock(priv);
++
++    args.name = dev->name;
++
++    if (call(dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DESTROY,
++             (xdrproc_t) xdr_remote_node_device_destroy_args, (char *) &args,
++             (xdrproc_t) xdr_void, (char *) NULL) == -1)
++        goto done;
++
++    rv = 0;
++
++done:
++    remoteDriverUnlock(priv);
++    return rv;
++}
++
++
+ /*----------------------------------------------------------------------*/
+ static int
+@@ -6982,6 +7035,8 @@ static virDeviceMonitor dev_monitor = {
+     .deviceGetParent = remoteNodeDeviceGetParent,
+     .deviceNumOfCaps = remoteNodeDeviceNumOfCaps,
+     .deviceListCaps = remoteNodeDeviceListCaps,
++    .deviceCreateXML = remoteNodeDeviceCreateXML,
++    .deviceDestroy = remoteNodeDeviceDestroy
+ };
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch b/docs/api_extension/0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch
new file mode 100644 (file)
index 0000000..96df453
--- /dev/null
@@ -0,0 +1,71 @@
+From 4c5166df583459574526841234d61d6ae5be19a0 Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:26:55 -0400
+Subject: [PATCH] Step 6 of 8 Implement the server side dispatcher
+
+---
+ qemud/remote.c |   48 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 48 insertions(+), 0 deletions(-)
+
+diff --git a/qemud/remote.c b/qemud/remote.c
+index e27820f..8d24a3a 100644
+--- a/qemud/remote.c
++++ b/qemud/remote.c
+@@ -4323,6 +4323,54 @@ remoteDispatchNodeDeviceReset (struct qemud_server *server ATTRIBUTE_UNUSED,
+ }
++static int
++remoteDispatchNodeDeviceCreateXml(struct qemud_server *server ATTRIBUTE_UNUSED,
++                                  struct qemud_client *client ATTRIBUTE_UNUSED,
++                                  virConnectPtr conn,
++                                  remote_error *rerr,
++                                  remote_node_device_create_xml_args *args,
++                                  remote_node_device_create_xml_ret *ret)
++{
++    virNodeDevicePtr dev;
++
++    dev = virNodeDeviceCreateXML (conn, args->xml_desc, args->flags);
++    if (dev == NULL) {
++        remoteDispatchConnError(rerr, conn);
++        return -1;
++    }
++
++    make_nonnull_node_device (&ret->dev, dev);
++    virNodeDeviceFree(dev);
++
++    return 0;
++}
++
++
++static int
++remoteDispatchNodeDeviceDestroy(struct qemud_server *server ATTRIBUTE_UNUSED,
++                                struct qemud_client *client ATTRIBUTE_UNUSED,
++                                virConnectPtr conn,
++                                remote_error *rerr,
++                                remote_node_device_destroy_args *args,
++                                void *ret ATTRIBUTE_UNUSED)
++{
++    virNodeDevicePtr dev;
++
++    dev = virNodeDeviceLookupByName(conn, args->name);
++    if (dev == NULL) {
++        remoteDispatchFormatError(rerr, "%s", _("node_device not found"));
++        return -1;
++    }
++
++    if (virNodeDeviceDestroy(dev) == -1) {
++        remoteDispatchConnError(rerr, conn);
++        return -1;
++    }
++
++    return 0;
++}
++
++
+ /**************************
+  * Async Events
+  **************************/
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0007-Step-7-of-8-Implement-the-driver-methods.patch b/docs/api_extension/0007-Step-7-of-8-Implement-the-driver-methods.patch
new file mode 100644 (file)
index 0000000..ddd0a41
--- /dev/null
@@ -0,0 +1,1172 @@
+From 04d20a662109de6727232eb1213627877bb9662f Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:35:15 -0400
+Subject: [PATCH] Step 7 of 8 Implement the driver methods
+
+---
+ src/Makefile.am             |    4 +-
+ src/node_device.c           |  430 +++++++++++++++++++++++++++++++++++++++++++
+ src/node_device.h           |   13 ++
+ src/node_device_conf.c      |  136 ++++++++++++--
+ src/node_device_conf.h      |   22 ++-
+ src/node_device_hal.c       |    5 +
+ src/node_device_hal.h       |   40 ++++
+ src/node_device_hal_linux.c |  170 +++++++++++++++++
+ src/qemu_driver.c           |    2 +-
+ src/storage_backend.c       |   24 +--
+ src/xen_unified.c           |    2 +-
+ tests/nodedevxml2xmltest.c  |    2 +-
+ 12 files changed, 810 insertions(+), 40 deletions(-)
+ create mode 100644 src/node_device_hal.h
+ create mode 100644 src/node_device_hal_linux.c
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index fd692b4..39fabce 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -188,7 +188,9 @@ NODE_DEVICE_DRIVER_SOURCES =                                       \
+               node_device.c node_device.h
+ NODE_DEVICE_DRIVER_HAL_SOURCES =                              \
+-              node_device_hal.c
++              node_device_hal.c                               \
++              node_device_hal_linux.c
++
+ NODE_DEVICE_DRIVER_DEVKIT_SOURCES =                           \
+               node_device_devkit.c
+diff --git a/src/node_device.c b/src/node_device.c
+index b84729f..4f73baf 100644
+--- a/src/node_device.c
++++ b/src/node_device.c
+@@ -25,6 +25,8 @@
+ #include <unistd.h>
+ #include <errno.h>
++#include <fcntl.h>
++#include <time.h>
+ #include "virterror_internal.h"
+ #include "datatypes.h"
+@@ -133,6 +135,53 @@ cleanup:
+     return ret;
+ }
++
++static virNodeDevicePtr
++nodeDeviceLookupByWWN(virConnectPtr conn,
++                      const char *wwnn,
++                      const char *wwpn)
++{
++    unsigned int i;
++    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
++    virNodeDeviceObjListPtr devs = &driver->devs;
++    virNodeDevCapsDefPtr cap = NULL;
++    virNodeDeviceObjPtr obj = NULL;
++    virNodeDevicePtr dev = NULL;
++
++    nodeDeviceLock(driver);
++
++    for (i = 0; i < devs->count; i++) {
++
++        obj = devs->objs[i];
++        virNodeDeviceObjLock(obj);
++        cap = obj->def->caps;
++
++        while (cap) {
++
++            if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST) {
++                if (cap->data.scsi_host.flags &
++                    VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
++
++                    if (STREQ(cap->data.scsi_host.wwnn, wwnn) &&
++                        STREQ(cap->data.scsi_host.wwpn, wwpn)) {
++                        dev = virGetNodeDevice(conn, obj->def->name);
++                        virNodeDeviceObjUnlock(obj);
++                        goto out;
++                    }
++                }
++            }
++            cap = cap->next;
++        }
++
++        virNodeDeviceObjUnlock(obj);
++    }
++
++out:
++    nodeDeviceUnlock(driver);
++    return dev;
++}
++
++
+ static char *nodeDeviceDumpXML(virNodeDevicePtr dev,
+                                unsigned int flags ATTRIBUTE_UNUSED)
+ {
+@@ -258,6 +307,385 @@ cleanup:
+ }
++static int
++nodeDeviceVportCreateDelete(virConnectPtr conn,
++                            const int parent_host,
++                            const char *wwpn,
++                            const char *wwnn,
++                            int operation)
++{
++    int fd = -1;
++    int retval = 0;
++    char *operation_path = NULL, *vport_name = NULL;
++    const char *operation_file = NULL;
++    size_t towrite = 0;
++    unsigned int written = 0;
++
++    switch (operation) {
++    case VPORT_CREATE:
++        operation_file = LINUX_SYSFS_VPORT_CREATE_POSTFIX;
++        break;
++    case VPORT_DELETE:
++        operation_file = LINUX_SYSFS_VPORT_DELETE_POSTFIX;
++        break;
++    default:
++        virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
++                                 _("Invalid vport operation (%d)"), operation);
++        retval = -1;
++        goto cleanup;
++        break;
++    }
++
++    if (virAsprintf(&operation_path,
++                    "%shost%d%s",
++                    LINUX_SYSFS_FC_HOST_PREFIX,
++                    parent_host,
++                    operation_file) < 0) {
++
++        virReportOOMError(conn);
++        retval = -1;
++        goto cleanup;
++    }
++
++    VIR_DEBUG(_("Vport operation path is '%s'"), operation_path);
++
++    fd = open(operation_path, O_WRONLY);
++
++    if (fd < 0) {
++        virReportSystemError(conn, errno,
++                             _("Could not open '%s' for vport operation"),
++                             operation_path);
++        retval = -1;
++        goto cleanup;
++    }
++
++    if (virAsprintf(&vport_name,
++                    "%s:%s",
++                    wwpn,
++                    wwnn) < 0) {
++
++        virReportOOMError(conn);
++        retval = -1;
++        goto cleanup;
++    }
++
++    towrite = strlen(vport_name);
++    written = safewrite(fd, vport_name, towrite);
++    if (written != towrite) {
++        virReportSystemError(conn, errno,
++                             _("Write of '%s' to '%s' during "
++                               "vport create/delete failed "
++                               "(towrite: %lu written: %d)"),
++                             vport_name, operation_path,
++                             towrite, written);
++        retval = -1;
++    }
++
++cleanup:
++    if (fd != -1) {
++        close(fd);
++    }
++    VIR_FREE(vport_name);
++    VIR_FREE(operation_path);
++    VIR_DEBUG("%s", _("Vport operation complete"));
++    return retval;
++}
++
++
++static int
++get_wwns(virConnectPtr conn,
++         virNodeDeviceDefPtr def,
++         char **wwnn,
++         char **wwpn)
++{
++    virNodeDevCapsDefPtr cap = NULL;
++    int ret = 0;
++
++    cap = def->caps;
++    while (cap != NULL) {
++        if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST &&
++            cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
++            *wwnn = strdup(cap->data.scsi_host.wwnn);
++            *wwpn = strdup(cap->data.scsi_host.wwpn);
++            break;
++        }
++
++        cap = cap->next;
++    }
++
++    if (cap == NULL) {
++        virNodeDeviceReportError(conn, VIR_ERR_NO_SUPPORT,
++                                 "%s", _("Device is not a fibre channel HBA"));
++        ret = -1;
++    }
++
++    if (*wwnn == NULL || *wwpn == NULL) {
++        /* Free the other one, if allocated... */
++        VIR_FREE(wwnn);
++        VIR_FREE(wwpn);
++        ret = -1;
++        virReportOOMError(conn);
++    }
++
++    return ret;
++}
++
++
++static int
++get_parent_host(virConnectPtr conn,
++                virDeviceMonitorStatePtr driver,
++                const char *dev_name,
++                const char *parent_name,
++                int *parent_host)
++{
++    virNodeDeviceObjPtr parent = NULL;
++    virNodeDevCapsDefPtr cap = NULL;
++    int ret = 0;
++
++    parent = virNodeDeviceFindByName(&driver->devs, parent_name);
++    if (parent == NULL) {
++        virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE,
++                                 _("Could not find parent device for '%s'"),
++                                 dev_name);
++        ret = -1;
++        goto out;
++    }
++
++    cap = parent->def->caps;
++    while (cap != NULL) {
++        if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST &&
++            (cap->data.scsi_host.flags &
++             VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)) {
++                *parent_host = cap->data.scsi_host.host;
++                break;
++        }
++
++        cap = cap->next;
++    }
++
++    if (cap == NULL) {
++        virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE,
++                                 _("Device %s is not capable of vport operations"),
++                                 parent->def->name);
++        ret = -1;
++    }
++
++    virNodeDeviceObjUnlock(parent);
++
++out:
++    return ret;
++}
++
++
++static int
++get_time(virConnectPtr conn, time_t *t)
++{
++    int ret = 0;
++
++    *t = time(NULL);
++    if (*t == (time_t)-1) {
++        virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
++                                 "%s", _("Could not get current time"));
++
++        *t = 0;
++        ret = -1;
++    }
++
++    return ret;
++}
++
++
++/* When large numbers of devices are present on the host, it's
++ * possible for udev not to realize that it has work to do before we
++ * get here.  We thus keep trying to find the new device we just
++ * created for up to LINUX_NEW_DEVICE_WAIT_TIME.  Note that udev's
++ * default settle time is 180 seconds, so once udev realizes that it
++ * has work to do, it might take that long for the udev wait to
++ * return.  Thus the total maximum time for this function to return is
++ * the udev settle time plus LINUX_NEW_DEVICE_WAIT_TIME.
++ *
++ * This whole area is a race, but if we retry the udev wait for
++ * LINUX_NEW_DEVICE_WAIT_TIME seconds and there's still no device,
++ * it's probably safe to assume it's not going to appear.
++ */
++static virNodeDevicePtr
++find_new_device(virConnectPtr conn, const char *wwnn, const char *wwpn)
++{
++    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
++    virNodeDevicePtr dev = NULL;
++    time_t start = 0, now = 0;
++
++    /* The thread that creates the device takes the driver lock, so we
++     * must release it in order to allow the device to be created.
++     * We're not doing anything with the driver pointer at this point,
++     * so it's safe to release it, assuming that the pointer itself
++     * doesn't become invalid.  */
++    nodeDeviceUnlock(driver);
++
++    get_time(conn, &start);
++
++    while ((now - start) < LINUX_NEW_DEVICE_WAIT_TIME) {
++
++        virNodeDeviceWaitForDevices(conn);
++
++        dev = nodeDeviceLookupByWWN(conn, wwnn, wwpn);
++
++        if (dev != NULL) {
++            break;
++        }
++
++        sleep(5);
++        if (get_time(conn, &now) == -1) {
++            break;
++        }
++    }
++
++    nodeDeviceLock(driver);
++
++    return dev;
++}
++
++static virNodeDevicePtr
++nodeDeviceCreateXML(virConnectPtr conn,
++                    const char *xmlDesc,
++                    unsigned int flags ATTRIBUTE_UNUSED)
++{
++    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
++    virNodeDeviceDefPtr def = NULL;
++    char *wwnn = NULL, *wwpn = NULL;
++    int parent_host = -1;
++    virNodeDevicePtr dev = NULL;
++
++    nodeDeviceLock(driver);
++
++    def = virNodeDeviceDefParseString(conn, xmlDesc, CREATE_DEVICE);
++    if (def == NULL) {
++        goto cleanup;
++    }
++
++    if (get_wwns(conn, def, &wwnn, &wwpn) == -1) {
++        goto cleanup;
++    }
++
++    if (get_parent_host(conn,
++                        driver,
++                        def->name,
++                        def->parent,
++                        &parent_host) == -1) {
++        goto cleanup;
++    }
++
++    if (nodeDeviceVportCreateDelete(conn,
++                                    parent_host,
++                                    wwpn,
++                                    wwnn,
++                                    VPORT_CREATE) == -1) {
++        goto cleanup;
++    }
++
++    dev = find_new_device(conn, wwnn, wwpn);
++    /* We don't check the return value, because one way or another,
++     * we're returning what we get... */
++
++    if (dev == NULL) {
++        virNodeDeviceReportError(conn, VIR_ERR_NO_NODE_DEVICE, NULL);
++    }
++
++cleanup:
++    nodeDeviceUnlock(driver);
++    virNodeDeviceDefFree(def);
++    VIR_FREE(wwnn);
++    VIR_FREE(wwpn);
++    return dev;
++}
++
++
++static int
++nodeDeviceDestroy(virNodeDevicePtr dev)
++{
++    int ret = 0;
++    virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData;
++    virNodeDeviceObjPtr obj = NULL;
++    char *parent_name = NULL, *wwnn = NULL, *wwpn = NULL;
++    int parent_host = -1;
++
++    nodeDeviceLock(driver);
++    obj = virNodeDeviceFindByName(&driver->devs, dev->name);
++    nodeDeviceUnlock(driver);
++
++    if (!obj) {
++        virNodeDeviceReportError(dev->conn, VIR_ERR_NO_NODE_DEVICE, NULL);
++        goto out;
++    }
++
++    if (get_wwns(dev->conn, obj->def, &wwnn, &wwpn) == -1) {
++        goto out;
++    }
++
++    parent_name = strdup(obj->def->parent);
++
++    /* get_parent_host will cause the device object's lock to be
++     * taken, so we have to dup the parent's name and drop the lock
++     * before calling it.  We don't need the reference to the object
++     * any more once we have the parent's name.  */
++    virNodeDeviceObjUnlock(obj);
++    obj = NULL;
++
++    if (parent_name == NULL) {
++        virReportOOMError(dev->conn);
++        goto out;
++    }
++
++    if (get_parent_host(dev->conn,
++                        driver,
++                        dev->name,
++                        parent_name,
++                        &parent_host) == -1) {
++        goto out;
++    }
++
++    if (nodeDeviceVportCreateDelete(dev->conn,
++                                    parent_host,
++                                    wwpn,
++                                    wwnn,
++                                    VPORT_DELETE) == -1) {
++        goto out;
++    }
++
++out:
++    VIR_FREE(parent_name);
++    VIR_FREE(wwnn);
++    VIR_FREE(wwpn);
++    return ret;
++}
++
++
++#if defined(UDEVADM) || defined(UDEVSETTLE)
++void virNodeDeviceWaitForDevices(virConnectPtr conn)
++{
++#ifdef UDEVADM
++    const char *const settleprog[] = { UDEVADM, "settle", NULL };
++#else
++    const char *const settleprog[] = { UDEVSETTLE, NULL };
++#endif
++    int exitstatus;
++
++    if (access(settleprog[0], X_OK) != 0)
++        return;
++
++    /*
++     * NOTE: we ignore errors here; this is just to make sure that any device
++     * nodes that are being created finish before we try to scan them.
++     * If this fails for any reason, we still have the backup of polling for
++     * 5 seconds for device nodes.
++     */
++    virRun(conn, settleprog, &exitstatus);
++}
++#else
++void virNodeDeviceWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
++#endif
++
++
+ void registerCommonNodeFuncs(virDeviceMonitorPtr driver)
+ {
+     driver->numOfDevices = nodeNumOfDevices;
+@@ -267,6 +695,8 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr driver)
+     driver->deviceGetParent = nodeDeviceGetParent;
+     driver->deviceNumOfCaps = nodeDeviceNumOfCaps;
+     driver->deviceListCaps = nodeDeviceListCaps;
++    driver->deviceCreateXML = nodeDeviceCreateXML;
++    driver->deviceDestroy = nodeDeviceDestroy;
+ }
+diff --git a/src/node_device.h b/src/node_device.h
+index 9496120..882ba0f 100644
+--- a/src/node_device.h
++++ b/src/node_device.h
+@@ -28,6 +28,17 @@
+ #include "driver.h"
+ #include "node_device_conf.h"
++#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
++#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device"
++#define LINUX_SYSFS_FC_HOST_PREFIX "/sys/class/fc_host/"
++
++#define VPORT_CREATE 0
++#define VPORT_DELETE 1
++#define LINUX_SYSFS_VPORT_CREATE_POSTFIX "/vport_create"
++#define LINUX_SYSFS_VPORT_DELETE_POSTFIX "/vport_delete"
++
++#define LINUX_NEW_DEVICE_WAIT_TIME 60
++
+ #ifdef HAVE_HAL
+ int halNodeRegister(void);
+ #endif
+@@ -42,4 +53,6 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr mon);
+ int nodedevRegister(void);
++void virNodeDeviceWaitForDevices(virConnectPtr conn);
++
+ #endif /* __VIR_NODE_DEVICE_H__ */
+diff --git a/src/node_device_conf.c b/src/node_device_conf.c
+index 6e04112..5b35b60 100644
+--- a/src/node_device_conf.c
++++ b/src/node_device_conf.c
+@@ -53,9 +53,34 @@ VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST,
+               "80203",
+               "80211")
++VIR_ENUM_IMPL(virNodeDevHBACap, VIR_NODE_DEV_CAP_HBA_LAST,
++              "fc_host",
++              "vport_ops")
+ #define virNodeDeviceLog(msg...) fprintf(stderr, msg)
++static int
++virNodeDevCapsDefParseString(virConnectPtr conn,
++                             const char *xpath,
++                             xmlXPathContextPtr ctxt,
++                             char **string,
++                             virNodeDeviceDefPtr def,
++                             const char *missing_error_fmt)
++{
++    char *s;
++
++    s = virXPathString(conn, xpath, ctxt);
++    if (s == NULL) {
++        virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
++                                 missing_error_fmt,
++                                 def->name);
++        return -1;
++    }
++
++    *string = s;
++    return 0;
++}
++
+ virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
+                                             const char *name)
+ {
+@@ -302,6 +327,18 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
+         case VIR_NODE_DEV_CAP_SCSI_HOST:
+             virBufferVSprintf(&buf, "    <host>%d</host>\n",
+                               data->scsi_host.host);
++            if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
++                virBufferAddLit(&buf, "    <capability type='fc_host'>\n");
++                virBufferVSprintf(&buf,
++                                  "      <wwnn>%s</wwnn>\n", data->scsi_host.wwnn);
++                virBufferVSprintf(&buf,
++                                  "      <wwpn>%s</wwpn>\n", data->scsi_host.wwpn);
++                virBufferAddLit(&buf, "    </capability>\n");
++            }
++            if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) {
++                virBufferAddLit(&buf, "    <capability type='vport_ops' />\n");
++            }
++
+             break;
+         case VIR_NODE_DEV_CAP_SCSI:
+             virBufferVSprintf(&buf, "    <host>%d</host>\n", data->scsi.host);
+@@ -561,26 +598,91 @@ virNodeDevCapScsiHostParseXML(virConnectPtr conn,
+                               xmlXPathContextPtr ctxt,
+                               virNodeDeviceDefPtr def,
+                               xmlNodePtr node,
+-                              union _virNodeDevCapData *data)
++                              union _virNodeDevCapData *data,
++                              int create)
+ {
+-    xmlNodePtr orignode;
+-    int ret = -1;
++    xmlNodePtr orignode, *nodes = NULL;
++    int ret = -1, n = 0, i;
++    char *type = NULL;
+     orignode = ctxt->node;
+     ctxt->node = node;
+-    if (virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt,
++    if (create == EXISTING_DEVICE &&
++        virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt,
+                                     &data->scsi_host.host, def,
+                                     _("no SCSI host ID supplied for '%s'"),
+-                                    _("invalid SCSI host ID supplied for '%s'")) < 0)
++                                    _("invalid SCSI host ID supplied for '%s'")) < 0) {
+         goto out;
++    }
++
++    if ((n = virXPathNodeSet(conn, "./capability", ctxt, &nodes)) < 0) {
++        virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
++                                 _("error parsing SCSI host capabilities for '%s'"),
++                                 def->name);
++        goto out;
++    }
++
++    for (i = 0 ; i < n ; i++) {
++        type = virXMLPropString(nodes[i], "type");
++
++        if (!type) {
++            virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
++                                     _("missing SCSI host capability type for '%s'"),
++                                     def->name);
++            goto out;
++        }
++
++        if (STREQ(type, "vport_ops")) {
++
++            data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
++
++        } else if (STREQ(type, "fc_host")) {
++
++            xmlNodePtr orignode2;
++
++            data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
++
++            orignode2 = ctxt->node;
++            ctxt->node = nodes[i];
++
++            if (virNodeDevCapsDefParseString(conn, "string(./wwnn[1])",
++                                             ctxt,
++                                             &data->scsi_host.wwnn,
++                                             def,
++                                             _("no WWNN supplied for '%s'")) < 0) {
++                goto out;
++            }
++
++            if (virNodeDevCapsDefParseString(conn, "string(./wwpn[1])",
++                                             ctxt,
++                                             &data->scsi_host.wwpn,
++                                             def,
++                                             _("no WWPN supplied for '%s'")) < 0) {
++                goto out;
++            }
++
++            ctxt->node = orignode2;
++
++        } else {
++            virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
++                                     _("unknown SCSI host capability type '%s' for '%s'"),
++                                     type, def->name);
++            goto out;
++        }
++
++        VIR_FREE(type);
++    }
+     ret = 0;
++
+ out:
++    VIR_FREE(type);
+     ctxt->node = orignode;
+     return ret;
+ }
++
+ static int
+ virNodeDevCapNetParseXML(virConnectPtr conn,
+                          xmlXPathContextPtr ctxt,
+@@ -848,7 +950,8 @@ static virNodeDevCapsDefPtr
+ virNodeDevCapsDefParseXML(virConnectPtr conn,
+                           xmlXPathContextPtr ctxt,
+                           virNodeDeviceDefPtr def,
+-                          xmlNodePtr node)
++                          xmlNodePtr node,
++                          int create)
+ {
+     virNodeDevCapsDefPtr caps;
+     char *tmp;
+@@ -892,7 +995,7 @@ virNodeDevCapsDefParseXML(virConnectPtr conn,
+         ret = virNodeDevCapNetParseXML(conn, ctxt, def, node, &caps->data);
+         break;
+     case VIR_NODE_DEV_CAP_SCSI_HOST:
+-        ret = virNodeDevCapScsiHostParseXML(conn, ctxt, def, node, &caps->data);
++        ret = virNodeDevCapScsiHostParseXML(conn, ctxt, def, node, &caps->data, create);
+         break;
+     case VIR_NODE_DEV_CAP_SCSI:
+         ret = virNodeDevCapScsiParseXML(conn, ctxt, def, node, &caps->data);
+@@ -918,7 +1021,7 @@ error:
+ }
+ static virNodeDeviceDefPtr
+-virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt)
++virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt, int create)
+ {
+     virNodeDeviceDefPtr def;
+     virNodeDevCapsDefPtr *next_cap;
+@@ -931,7 +1034,12 @@ virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt)
+     }
+     /* Extract device name */
+-    def->name = virXPathString(conn, "string(./name[1])", ctxt);
++    if (create == EXISTING_DEVICE) {
++        def->name = virXPathString(conn, "string(./name[1])", ctxt);
++    } else {
++        def->name = strdup("new device");
++    }
++
+     if (!def->name) {
+         virNodeDeviceReportError(conn, VIR_ERR_NO_NAME, NULL);
+         goto error;
+@@ -951,7 +1059,7 @@ virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt)
+     next_cap = &def->caps;
+     for (i = 0 ; i < n ; i++) {
+-        *next_cap = virNodeDevCapsDefParseXML(conn, ctxt, def, nodes[i]);
++        *next_cap = virNodeDevCapsDefParseXML(conn, ctxt, def, nodes[i], create);
+         if (!*next_cap) {
+             VIR_FREE(nodes);
+             goto error;
+@@ -969,7 +1077,7 @@ virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt)
+ }
+ static virNodeDeviceDefPtr
+-virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root)
++virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root, int create)
+ {
+     xmlXPathContextPtr ctxt = NULL;
+     virNodeDeviceDefPtr def = NULL;
+@@ -987,7 +1095,7 @@ virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root)
+     }
+     ctxt->node = root;
+-    def = virNodeDeviceDefParseXML(conn, ctxt);
++    def = virNodeDeviceDefParseXML(conn, ctxt, create);
+ cleanup:
+     xmlXPathFreeContext(ctxt);
+@@ -1015,7 +1123,7 @@ catchXMLError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+ }
+ virNodeDeviceDefPtr
+-virNodeDeviceDefParseString(virConnectPtr conn, const char *str)
++virNodeDeviceDefParseString(virConnectPtr conn, const char *str, int create)
+ {
+     xmlParserCtxtPtr pctxt;
+     xmlDocPtr xml = NULL;
+@@ -1046,7 +1154,7 @@ virNodeDeviceDefParseString(virConnectPtr conn, const char *str)
+         goto cleanup;
+     }
+-    def = virNodeDeviceDefParseNode(conn, xml, root);
++    def = virNodeDeviceDefParseNode(conn, xml, root, create);
+ cleanup:
+     xmlFreeParserCtxt(pctxt);
+diff --git a/src/node_device_conf.h b/src/node_device_conf.h
+index 26e5558..62b4e71 100644
+--- a/src/node_device_conf.h
++++ b/src/node_device_conf.h
+@@ -28,6 +28,9 @@
+ #include "util.h"
+ #include "threads.h"
++#define CREATE_DEVICE 1
++#define EXISTING_DEVICE 0
++
+ enum virNodeDevCapType {
+     /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
+     VIR_NODE_DEV_CAP_SYSTEM,          /* System capability */
+@@ -48,8 +51,16 @@ enum virNodeDevNetCapType {
+     VIR_NODE_DEV_CAP_NET_LAST
+ };
++enum virNodeDevHBACapType {
++    /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
++    VIR_NODE_DEV_CAP_HBA_FC_HOST,     /* fibre channel HBA */
++    VIR_NODE_DEV_CAP_HBA_VPORT_OPS,   /* capable of vport operations */
++    VIR_NODE_DEV_CAP_HBA_LAST
++};
++
+ VIR_ENUM_DECL(virNodeDevCap)
+ VIR_ENUM_DECL(virNodeDevNetCap)
++VIR_ENUM_DECL(virNodeDevHBACap)
+ enum virNodeDevStorageCapFlags {
+     VIR_NODE_DEV_CAP_STORAGE_REMOVABLE                        = (1 << 0),
+@@ -57,6 +68,11 @@ enum virNodeDevStorageCapFlags {
+     VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE             = (1 << 2),
+ };
++enum virNodeDevScsiHostCapFlags {
++    VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST                 = (1 << 0),
++    VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS                       = (1 << 1),
++};
++
+ typedef struct _virNodeDevCapsDef virNodeDevCapsDef;
+ typedef virNodeDevCapsDef *virNodeDevCapsDefPtr;
+ struct _virNodeDevCapsDef {
+@@ -108,6 +124,9 @@ struct _virNodeDevCapsDef {
+         } net;
+         struct {
+             unsigned host;
++            char *wwnn;
++            char *wwpn;
++            unsigned flags;
+         } scsi_host;
+         struct {
+             unsigned host;
+@@ -185,7 +204,8 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
+                              const virNodeDeviceDefPtr def);
+ virNodeDeviceDefPtr virNodeDeviceDefParseString(virConnectPtr conn,
+-                                                const char *str);
++                                                const char *str,
++                                                int create);
+ void virNodeDeviceDefFree(virNodeDeviceDefPtr def);
+diff --git a/src/node_device_hal.c b/src/node_device_hal.c
+index b214f60..5927ba1 100644
+--- a/src/node_device_hal.c
++++ b/src/node_device_hal.c
+@@ -28,6 +28,7 @@
+ #include <libhal.h>
+ #include "node_device_conf.h"
++#include "node_device_hal.h"
+ #include "virterror_internal.h"
+ #include "driver.h"
+ #include "datatypes.h"
+@@ -37,6 +38,8 @@
+ #include "logging.h"
+ #include "node_device.h"
++#define VIR_FROM_THIS VIR_FROM_NODEDEV
++
+ /*
+  * Host device enumeration (HAL implementation)
+  */
+@@ -215,6 +218,8 @@ static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi,
+                                 union _virNodeDevCapData *d)
+ {
+     (void)get_int_prop(ctx, udi, "scsi_host.host", (int *)&d->scsi_host.host);
++    (void)check_fc_host(d);
++    (void)check_vport_capable(d);
+     return 0;
+ }
+diff --git a/src/node_device_hal.h b/src/node_device_hal.h
+new file mode 100644
+index 0000000..0b4a2ef
+--- /dev/null
++++ b/src/node_device_hal.h
+@@ -0,0 +1,40 @@
++/*
++ * node_device_hal.h: node device enumeration - HAL-based implementation
++ *
++ * Copyright (C) 2009 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
++ *
++ */
++
++#ifndef __VIR_NODE_DEVICE_HAL_H__
++#define __VIR_NODE_DEVICE_HAL_H__
++
++#ifdef __linux__
++
++#define check_fc_host(d) check_fc_host_linux(d)
++int check_fc_host_linux(union _virNodeDevCapData *d);
++
++#define check_vport_capable(d) check_vport_capable_linux(d)
++int check_vport_capable_linux(union _virNodeDevCapData *d);
++
++#else  /* __linux__ */
++
++#define check_fc_host(d)
++#define check_vport_capable(d)
++
++#endif /* __linux__ */
++
++#endif /* __VIR_NODE_DEVICE_HAL_H__ */
+diff --git a/src/node_device_hal_linux.c b/src/node_device_hal_linux.c
+new file mode 100644
+index 0000000..1deb6d2
+--- /dev/null
++++ b/src/node_device_hal_linux.c
+@@ -0,0 +1,170 @@
++/*
++ * node_device_hal_linuc.c: Linux specific code to gather device data
++ * not available through HAL.
++ *
++ * Copyright (C) 2009 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
++ *
++ */
++
++#include <config.h>
++
++#include <fcntl.h>
++
++#include "node_device.h"
++#include "node_device_hal.h"
++#include "virterror_internal.h"
++#include "memory.h"
++#include "logging.h"
++
++#define VIR_FROM_THIS VIR_FROM_NODEDEV
++
++#ifdef __linux__
++
++int check_fc_host_linux(union _virNodeDevCapData *d)
++{
++    char *sysfs_path = NULL;
++    char *wwnn_path = NULL;
++    char *wwpn_path = NULL;
++    char *p = NULL;
++    int fd = -1;
++    char buf[64];
++    struct stat st;
++
++    VIR_DEBUG(_("Checking if host%d is an FC HBA"), d->scsi_host.host);
++
++    if (virAsprintf(&sysfs_path, "%s/host%d",
++                    LINUX_SYSFS_FC_HOST_PREFIX,
++                    d->scsi_host.host) < 0) {
++        virReportOOMError(NULL);
++        goto out;
++    }
++
++    if (stat(sysfs_path, &st) != 0) {
++        /* Not an FC HBA */
++        goto out;
++    }
++
++    d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
++
++    if (virAsprintf(&wwnn_path, "%s/node_name",
++                    sysfs_path) < 0) {
++        virReportOOMError(NULL);
++        goto out;
++    }
++
++    if ((fd = open(wwnn_path, O_RDONLY)) < 0) {
++        goto out;
++    }
++
++    memset(buf, 0, sizeof(buf));
++    if (saferead(fd, buf, sizeof(buf)) < 0) {
++        goto out;
++    }
++
++    close(fd);
++    fd = -1;
++
++    p = strstr(buf, "0x");
++    if (p != NULL) {
++        p += strlen("0x");
++    } else {
++        p = buf;
++    }
++
++    d->scsi_host.wwnn = strndup(p, sizeof(buf));
++    if (d->scsi_host.wwnn == NULL) {
++        virReportOOMError(NULL);
++        goto out;
++    }
++
++    p = strchr(d->scsi_host.wwnn, '\n');
++    if (p != NULL) {
++        *p = '\0';
++    }
++
++    if (virAsprintf(&wwpn_path, "%s/port_name",
++                    sysfs_path) < 0) {
++        virReportOOMError(NULL);
++        goto out;
++    }
++
++    if ((fd = open(wwpn_path, O_RDONLY)) < 0) {
++        goto out;
++    }
++
++    memset(buf, 0, sizeof(buf));
++    if (saferead(fd, buf, sizeof(buf)) < 0) {
++        goto out;
++    }
++
++    close(fd);
++    fd = -1;
++
++    p = strstr(buf, "0x");
++    if (p != NULL) {
++        p += strlen("0x");
++    } else {
++        p = buf;
++    }
++
++    d->scsi_host.wwpn = strndup(p, sizeof(buf));
++    if (d->scsi_host.wwpn == NULL) {
++        virReportOOMError(NULL);
++        goto out;
++    }
++
++    p = strchr(d->scsi_host.wwpn, '\n');
++    if (p != NULL) {
++        *p = '\0';
++    }
++
++out:
++    if (fd != -1) {
++        close(fd);
++    }
++    VIR_FREE(sysfs_path);
++    VIR_FREE(wwnn_path);
++    VIR_FREE(wwpn_path);
++    return 0;
++}
++
++
++int check_vport_capable_linux(union _virNodeDevCapData *d)
++{
++    char *sysfs_path = NULL;
++    struct stat st;
++
++    if (virAsprintf(&sysfs_path, "%s/host%d/vport_create",
++                    LINUX_SYSFS_FC_HOST_PREFIX,
++                    d->scsi_host.host) < 0) {
++        virReportOOMError(NULL);
++        goto out;
++    }
++
++    if (stat(sysfs_path, &st) != 0) {
++        /* Not a vport capable HBA */
++        goto out;
++    }
++
++    d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
++
++out:
++    VIR_FREE(sysfs_path);
++    return 0;
++}
++
++#endif /* __linux__ */
+diff --git a/src/qemu_driver.c b/src/qemu_driver.c
+index bd60b29..057e97b 100644
+--- a/src/qemu_driver.c
++++ b/src/qemu_driver.c
+@@ -5089,7 +5089,7 @@ qemudNodeDeviceGetPciInfo (virNodeDevicePtr dev,
+     if (!xml)
+         goto out;
+-    def = virNodeDeviceDefParseString(dev->conn, xml);
++    def = virNodeDeviceDefParseString(dev->conn, xml, EXISTING_DEVICE);
+     if (!def)
+         goto out;
+diff --git a/src/storage_backend.c b/src/storage_backend.c
+index b154140..74759cf 100644
+--- a/src/storage_backend.c
++++ b/src/storage_backend.c
+@@ -46,6 +46,7 @@
+ #include "virterror_internal.h"
+ #include "util.h"
+ #include "memory.h"
++#include "node_device.h"
+ #include "storage_backend.h"
+@@ -245,30 +246,11 @@ virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
+     return 0;
+ }
+-#if defined(UDEVADM) || defined(UDEVSETTLE)
+ void virStorageBackendWaitForDevices(virConnectPtr conn)
+ {
+-#ifdef UDEVADM
+-    const char *const settleprog[] = { UDEVADM, "settle", NULL };
+-#else
+-    const char *const settleprog[] = { UDEVSETTLE, NULL };
+-#endif
+-    int exitstatus;
+-
+-    if (access(settleprog[0], X_OK) != 0)
+-        return;
+-
+-    /*
+-     * NOTE: we ignore errors here; this is just to make sure that any device
+-     * nodes that are being created finish before we try to scan them.
+-     * If this fails for any reason, we still have the backup of polling for
+-     * 5 seconds for device nodes.
+-     */
+-    virRun(conn, settleprog, &exitstatus);
++    virNodeDeviceWaitForDevices(conn);
++    return;
+ }
+-#else
+-void virStorageBackendWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
+-#endif
+ /*
+  * Given a volume path directly in /dev/XXX, iterate over the
+diff --git a/src/xen_unified.c b/src/xen_unified.c
+index e708980..8da4e23 100644
+--- a/src/xen_unified.c
++++ b/src/xen_unified.c
+@@ -1439,7 +1439,7 @@ xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev,
+     if (!xml)
+         goto out;
+-    def = virNodeDeviceDefParseString(dev->conn, xml);
++    def = virNodeDeviceDefParseString(dev->conn, xml, EXISTING_DEVICE);
+     if (!def)
+         goto out;
+diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c
+index 29cdb9e..7621212 100644
+--- a/tests/nodedevxml2xmltest.c
++++ b/tests/nodedevxml2xmltest.c
+@@ -29,7 +29,7 @@ static int testCompareXMLToXMLFiles(const char *xml) {
+     if (virtTestLoadFile(xml, &xmlPtr, MAX_FILE) < 0)
+         goto fail;
+-    if (!(dev = virNodeDeviceDefParseString(NULL, xmlData)))
++    if (!(dev = virNodeDeviceDefParseString(NULL, xmlData, EXISTING_DEVICE)))
+         goto fail;
+     if (!(actual = virNodeDeviceDefFormat(NULL, dev)))
+-- 
+1.6.0.6
+
diff --git a/docs/api_extension/0008-Step-8-of-8-Add-virsh-support.patch b/docs/api_extension/0008-Step-8-of-8-Add-virsh-support.patch
new file mode 100644 (file)
index 0000000..55b758b
--- /dev/null
@@ -0,0 +1,133 @@
+From 193cc4abbb6c2fc5557d3699f86ff0103d5a21ef Mon Sep 17 00:00:00 2001
+From: David Allan <dallan@redhat.com>
+Date: Tue, 19 May 2009 16:47:31 -0400
+Subject: [PATCH 8/8] Step 8 of 8 Add virsh support
+
+---
+ src/virsh.c |  103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 files changed, 103 insertions(+), 0 deletions(-)
+
+diff --git a/src/virsh.c b/src/virsh.c
+index cb32ede..ab2a2b7 100644
+--- a/src/virsh.c
++++ b/src/virsh.c
+@@ -2962,6 +2962,107 @@ cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
+ /*
++ * "nodedev-create" command
++ */
++static const vshCmdInfo info_node_device_create[] = {
++    {"help", gettext_noop("create a device defined "
++                          "by an XML file on the node")},
++    {"desc", gettext_noop("Create a device on the node.  Note that this "
++                          "command creates devices on the physical host "
++                          "that can then be assigned to a virtual machine.")},
++    {NULL, NULL}
++};
++
++static const vshCmdOptDef opts_node_device_create[] = {
++    {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
++     gettext_noop("file containing an XML description of the device")},
++    {NULL, 0, 0, NULL}
++};
++
++static int
++cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
++{
++    virNodeDevicePtr dev = NULL;
++    char *from;
++    int found = 0;
++    int ret = TRUE;
++    char *buffer;
++
++    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
++        return FALSE;
++
++    from = vshCommandOptString(cmd, "file", &found);
++    if (!found) {
++        return FALSE;
++    }
++
++    if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) {
++        return FALSE;
++    }
++
++    dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
++    free (buffer);
++
++    if (dev != NULL) {
++        vshPrint(ctl, _("Node device %s created from %s\n"),
++                 virNodeDeviceGetName(dev), from);
++    } else {
++        vshError(ctl, FALSE, _("Failed to create node device from %s"), from);
++        ret = FALSE;
++    }
++
++    return ret;
++}
++
++
++/*
++ * "nodedev-destroy" command
++ */
++static const vshCmdInfo info_node_device_destroy[] = {
++    {"help", gettext_noop("destroy a device on the node")},
++    {"desc", gettext_noop("Destroy a device on the node.  Note that this "
++                          "command destroys devices on the physical host ")},
++    {NULL, NULL}
++};
++
++static const vshCmdOptDef opts_node_device_destroy[] = {
++    {"name", VSH_OT_DATA, VSH_OFLAG_REQ,
++     gettext_noop("name of the device to be destroyed")},
++    {NULL, 0, 0, NULL}
++};
++
++static int
++cmdNodeDeviceDestroy(vshControl *ctl, const vshCmd *cmd)
++{
++    virNodeDevicePtr dev = NULL;
++    int ret = TRUE;
++    int found = 0;
++    char *name;
++
++    if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) {
++        return FALSE;
++    }
++
++    name = vshCommandOptString(cmd, "name", &found);
++    if (!found) {
++        return FALSE;
++    }
++
++    dev = virNodeDeviceLookupByName(ctl->conn, name);
++
++    if (virNodeDeviceDestroy(dev) == 0) {
++        vshPrint(ctl, _("Destroyed node device '%s'\n"), name);
++    } else {
++        vshError(ctl, FALSE, _("Failed to destroy node device '%s'"), name);
++        ret = FALSE;
++    }
++
++    virNodeDeviceFree(dev);
++    return ret;
++}
++
++
++/*
+  * XML Building helper for pool-define-as and pool-create-as
+  */
+ static const vshCmdOptDef opts_pool_X_as[] = {
+@@ -5895,6 +5996,8 @@ static const vshCmdDef commands[] = {
+     {"nodedev-dettach", cmdNodeDeviceDettach, opts_node_device_dettach, info_node_device_dettach},
+     {"nodedev-reattach", cmdNodeDeviceReAttach, opts_node_device_reattach, info_node_device_reattach},
+     {"nodedev-reset", cmdNodeDeviceReset, opts_node_device_reset, info_node_device_reset},
++    {"nodedev-create", cmdNodeDeviceCreate, opts_node_device_create, info_node_device_create},
++    {"nodedev-destroy", cmdNodeDeviceDestroy, opts_node_device_destroy, info_node_device_destroy},
+     {"pool-autostart", cmdPoolAutostart, opts_pool_autostart, info_pool_autostart},
+     {"pool-build", cmdPoolBuild, opts_pool_build, info_pool_build},
+-- 
+1.6.0.6
+
index a404c25d433bf10ef9d333829802f5fd7f45586c..09c8c5bb6eebe823d1606cbb7937d3537958e1e3 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 6f4f8c1a3771092174154bf33491c8f672dc0c21..d01c1d670ea9fb1fb2219d05e98c02043f591cd0 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index d2e4cdfcf0ab0bb3eab341481e05b50fc847b845..d5b6d303ce1a4f01d73781dca75ee9fa0259771d 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index cc33db8b590afcaaeb40db46caf4ff99b16e0c8f..5d08da87bcf03e6be1e774c1eb351ccf127ba15a 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 590093e66c341a0a8599637f47fb26a9c707064d..dd16d5cc5c40538688428a6eb41871a7289a78c1 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index e77846cd5b6076f0d9c20b686a088eefef22e767..8196a42e0d8294f5756674cd6a533bdeb3b2d200 100644 (file)
                         </div>
                       </li></ul>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 36cfbc7e90db94ff0671fc02faca8cb3520d3a48..da1423cac19d4200f1a54e50127a11c55bcacf89 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index f6eb610978b64dafad35d3fbea8051f082aa949c..49a62d54d17c0c2887e64f381e56e5f1fcc9ef46 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 27328883f6f7f76d87c956318575bad35edfc3df..e535ea7ea9638e6ff9710c24ddc6c1ca33f8c8a0 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 0f021fad3e0c1941ca99332d46297f60ae30f1b9..a8a4073b00131b400e31b7130a760f01914873d2 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index ac932dd481fbabf78423d28edf7b1f8dafd9c7ab..579fdf0a42cc2f208dd612c374a22ccb2bf4c615 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 20fff4704009da3eb11a2a9e406c34c37c49da89..6f1e271d0e314eaf295d4e4a15f5052fd933a851 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 5848eb29b13e1e0fbfcbf58e8fabe69544f33ec6..445f08595941a2c143a087ea5d05fc43a08c1474 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 0b4b9dc10e556572797f99bf564c7f7fd4acec22..efcfec3f4dc6b0bd8f5089c5527ca3d68e0de371 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 6b66ac789692d2a10cb6d1babbcd83d53282b368..f4c187ed1be22aa223e926aa26c6f74e4ffd50ca 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index bdc3d3bbacfb746216eb278b44d45a5a55d172b2..be6ff9771316721e3e9a94ab87c4ca8841d02e9e 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 49449617c8f8568054d0b54a9b87f27150d06ad8..62327884db5d423cc07beb3530ec7d5f005e8387 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 624c8604df86d3f943cb42535e4c5f89d61eeca8..e97d0e747518b0cd2aff66cd7564e4f7b0117bac 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index e27bacd3f7b8e3cf1652c398a773525347ed9adf..5b20aacfe83fb41f3b2b17233db811f5e7c98358 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 27e42acb17beb178cafef1d87b13965367d5c2d1..e13cd690f8e7ab28bed2b1acd95b14afd2144ec1 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 59539c2af774f400d91d772bafac81351e43e838..0b25a0be1180104fa52e1be79b97b7561ed1460e 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 1593b69f0f6972d750c98d0fd5ee9f1d6f8fbd64..4d30b0c1d7bd4a262cd95c55110c29673d51d63c 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index e96712db3d23e8ea1ec3b920ed43e5ace7e08e94..91e63b4c3329a2b880ac7410c3742fa4749fbe81 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 5e24327f2a6dddab11c9aa38c17f3ff4fc0f5976..b25b0d333497eef13e1d98f79196fe8fdb6b26a3 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 5f903f83240bd7737132c75799cdd488f706f4b2..3e4ddb10f92c73ca1ae165f75c4b9732de97bd17 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
diff --git a/docs/internals.html b/docs/internals.html
new file mode 100644 (file)
index 0000000..8756214
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+        This file is autogenerated from internals.html.in
+        Do not edit this file. Changes will be lost.
+      -->
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+    <link rel="stylesheet" type="text/css" href="main.css" />
+    <link rel="SHORTCUT ICON" href="32favicon.png" />
+    <title>libvirt: libvirt internals</title>
+    <meta name="description" content="libvirt, virtualization, virtualization API" />
+  </head>
+  <body>
+    <div id="header">
+      <div id="headerLogo"></div>
+      <div id="headerSearch">
+        <form action="search.php" enctype="application/x-www-form-urlencoded" method="get"><div>
+            <input id="query" name="query" type="text" size="12" value="" />
+            <input id="submit" name="submit" type="submit" value="Search" />
+          </div></form>
+      </div>
+    </div>
+    <div id="body">
+      <div id="menu">
+        <ul class="l0"><li>
+            <div>
+              <a title="Front page of the libvirt website" class="inactive" href="index.html">Home</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Details of new features and bugs fixed in each release" class="inactive" href="news.html">News</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Get the latest source releases, binary builds and get access to the source repository" class="inactive" href="downloads.html">Downloads</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Information for users, administrators and developers" class="active" href="docs.html">Documentation</a>
+              <ul class="l1"><li>
+                  <div>
+                    <a title="Information about deploying and using libvirt" class="inactive" href="deployment.html">Deployment</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Overview of the logical subsystems in the libvirt API" class="inactive" href="intro.html">Architecture</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Description of the XML formats used in libvirt" class="inactive" href="format.html">XML format</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Hypervisor specific driver information" class="inactive" href="drivers.html">Drivers</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Reference manual for the C public API" class="inactive" href="html/index.html">API reference</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
+                  </div>
+                </li><li>
+                  <div>
+                    <span class="active">Internals</span>
+                    <ul class="l2"><li>
+                        <div>
+                          <a title="Adding new public libvirt APIs" class="inactive" href="api_extension.html">API extensions</a>
+                        </div>
+                      </li></ul>
+                  </div>
+                </li></ul>
+            </div>
+          </li><li>
+            <div>
+              <a title="User contributed content" class="inactive" href="http://wiki.libvirt.org">Wiki</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Frequently asked questions" class="inactive" href="FAQ.html">FAQ</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="How and where to report bugs and request features" class="inactive" href="bugs.html">Bug reports</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="How to contact the developers via email and IRC" class="inactive" href="contact.html">Contact</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Miscellaneous links of interest related to libvirt" class="inactive" href="relatedlinks.html">Related Links</a>
+            </div>
+          </li><li>
+            <div>
+              <a title="Overview of all content on the website" class="inactive" href="sitemap.html">Sitemap</a>
+            </div>
+          </li></ul>
+      </div>
+      <div id="content">
+        <h1>libvirt internals</h1>
+        <p>
+      This section provides documents useful to those working on the libvirt
+      internals, adding new public APIs, new hypervisor drivers or extending
+      the libvirtd daemon code.
+    </p>
+      </div>
+    </div>
+    <div id="footer">
+      <p id="sponsor">
+           Sponsored by:<br /><a href="http://et.redhat.com/"><img src="et.png" alt="Project sponsored by Red Hat Emerging Technology" /></a></p>
+    </div>
+  </body>
+</html>
diff --git a/docs/internals.html.in b/docs/internals.html.in
new file mode 100644 (file)
index 0000000..d39098e
--- /dev/null
@@ -0,0 +1,11 @@
+<html>
+  <body>
+    <h1>libvirt internals</h1>
+
+    <p>
+      This section provides documents useful to those working on the libvirt
+      internals, adding new public APIs, new hypervisor drivers or extending
+      the libvirtd daemon code.
+    </p>
+  </body>
+</html>
index c02e5bd985f5f9e2ba5862a1ca7af406441b2c69..524f73dd4a2da1f8256aca4c1d4532127c4c8c3c 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 572e583193b2ca85dfbddcf84fe79714a781d061..c1b4f65e887b594d9598f60e335f74c6a2984ce3 100644 (file)
                         </div>
                       </li></ul>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 167fb77710989addda8f43667d8e89bdf64989b9..ea00efab489230e13872e23b8a83a6797c1414ea 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index b4f57a14e87d3ca9bf6cc8d330cf6d9f4ada7279..e44e8c7d25ccad99e9b23153c503443b91759134 100644 (file)
                         </div>
                       </li></ul>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 391315677b6f97f22e31df18cd912a533a456249..30750bc980b93a969d4efe335e1861c8dd5e7b74 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index d03c64a00520589b3a822dcb7c3485c721935cd5..a6591e5e3bfeb9ccc44c8bbae00a6c8e8d76fdd5 100644 (file)
               </li><li>
                 <a href="java.html">Java</a>
                 <span>overview of the Java API bindings</span>
+              </li></ul></li><li>
+            <a href="internals.html">Internals</a>
+            <span>Working on the internals of libvirt API, driver and daemon code</span>
+            <ul><li>
+                <a href="api_extension.html">API extensions</a>
+                <span>Adding new public libvirt APIs</span>
               </li></ul></li></ul></li><li>
         <a href="http://wiki.libvirt.org">Wiki</a>
         <span>User contributed content</span>
index 00328e1714e3bf2d21ec2de6aad79419d7ee4dff..2e21530c043a22dad916b4d7efa615963026289a 100644 (file)
               </li>
             </ul>
           </li>
+          <li>
+            <a href="internals.html">Internals</a>
+            <span>Working on the internals of libvirt API, driver and daemon code</span>
+            <ul>
+              <li>
+                <a href="api_extension.html">API extensions</a>
+                <span>Adding new public libvirt APIs</span>
+              </li>
+            </ul>
+          </li>
         </ul>
       </li>
       <li>
index 94a8621faa4616eb612ef5265e9c5008248e7099..a4fb720264480c7e958d5738dc467b0cbf0cc01b 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index a663aad13fabcfcc64d2cd42b0f758c4bb5c8265..ee25bfc86dfb9cd1740248b06f3a0c978ce01b62 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>
index 7e9745eda24013150fc0e2ed0a703262c69dd319..39dd1f6ed6eb6fd1a652a0dc2333fa8d8ff9238e 100644 (file)
                   <div>
                     <a title="Bindings of the libvirt API for other languages" class="inactive" href="bindings.html">Language bindings</a>
                   </div>
+                </li><li>
+                  <div>
+                    <a title="Working on the internals of libvirt API, driver and daemon code" class="inactive" href="internals.html">Internals</a>
+                  </div>
                 </li></ul>
             </div>
           </li><li>