diff --git a/ChangeLog b/ChangeLog index f50523f89f..8621f9e053 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Tue Jun 9 12:39:24 BST 2009 Daniel P. Berrange + + 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 * src/xm_internals.c: the memory shrinking on device removal diff --git a/docs/Makefile.am b/docs/Makefile.am index 97b445686d..43d04c3c9e 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -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) diff --git a/docs/api.html b/docs/api.html index e799e51618..a1f3855567 100644 --- a/docs/api.html +++ b/docs/api.html @@ -88,6 +88,10 @@
Language bindings
+
  • +
    + Internals +
  • diff --git a/docs/api_extension.html b/docs/api_extension.html new file mode 100644 index 0000000000..933e5fd4fe --- /dev/null +++ b/docs/api_extension.html @@ -0,0 +1,361 @@ + + + + + + + + + libvirt: Implementing a new API in Libvirt + + + + +
    + +
    +

    Implementing a new API in Libvirt

    + +

    + 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. +

    +

    + 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. +

    +

    + 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: +

    +

    + http://libvirt.org/downloads.html +

    +

    + Once you have a working development environment, the steps to create a + new API are: +

    +
    1. define the public API
    2. define the internal driver API
    3. implement the public API
    4. define the wire protocol format
    5. implement the RPC client
    6. implement the server side dispatcher
    7. implement the driver methods
    8. add virsh support
    +

    + 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. +

    +

    + 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. +

    +

    + 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. +

    +

    With that said, let's begin.

    +

    + Defining the public API +

    +

    The first task is to define the public API and add it to:

    +

    + include/libvirt/libvirt.h.in +

    +

    + 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. +

    +

    Once you have defined the API, you have to add the symbol names to:

    +

    + src/libvirt_public.syms +

    +

    See 0001-Step-1-of-8-Define-the-public-API.patch for example code.

    +

    + Defining the internal API +

    +

    + 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. +

    +

    + 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. +

    +

    The driver structs are defined in:

    +

    + src/driver.h +

    +

    + To define the internal API, first typedef the driver function + prototype and then add a new field for it to the relevant driver + struct. +

    +

    See 0002-Step-2-of-8-Define-the-internal-driver-API.patch

    +

    + Implementing the public API +

    +

    + 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: +

    +
    1. SHOULD log a message with VIR_DEBUG() indicating that it is + being called and its parameters;
    2. MUST call virResetLastError();
    3. SHOULD confirm that the connection is valid with + VIR_IS_CONNECT(conn);
    4. 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;
    5. SHOULD do basic validation of the parameters that are being + passed in;
    6. MUST confirm that the driver for this connection exists and that + it implements this function;
    7. MUST call the internal API;
    8. SHOULD log a message with VIR_DEBUG() indicating that it is + returning, its return value, and status.
    9. MUST return status to the caller.
    +

    The public API calls are implemented in:

    +

    + src/libvirt.c +

    +

    See 0003-Step-3-of-8-Implement-the-public-API.patch

    +

    + Defining the wire protocol format +

    +

    + 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: +

    +

    + qemud/remote_protocol.x +

    +

    + 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. +

    +

    + Second, add values to the remote_procedure enum for each new function + added to the API. +

    +

    See 0004-Step-4-of-8-Define-the-wire-protocol-format.patch

    +

    + 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 +

    +

    + Implement the RPC client +

    +

    + 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: +

    +

    + src/remote_internal.c +

    +

    Each remote method invocation does the following:

    +
    1. locks the remote driver;
    2. sets up the method arguments;
    3. invokes the remote function;
    4. checks the return value, if necessary;
    5. extracts any returned data;
    6. frees any returned data;
    7. unlocks the remote driver.
    +

    + Once you have created the remote method calls, you have to add fields + for them to the driver structs for the appropriate remote driver. +

    +

    See 0005-Step-5-of-8-Implement-the-RPC-client.patch

    +

    + Implement the server side dispatcher +

    +

    + 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: +

    +

    + qemud/remote.c +

    +

    Again, this step uses the .h files generated by make rpcgen.

    +

    See 0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch

    +

    + Implement the driver methods +

    +

    + 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. +

    +

    + 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. +

    +

    + 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. +

    +

    See 0007-Step-7-of-8-Implement-the-driver-methods.patch

    +

    + Implement virsh commands +

    +

    + 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. +

    +

    + 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. +

    +

    See 0008-Step-8-of-8-Add-virsh-support.patch

    +

    Once you have working functionality, run make check and make + syntax-check before generating patches.

    +
    +
    + + + diff --git a/docs/api_extension.html.in b/docs/api_extension.html.in new file mode 100644 index 0000000000..2cbd2bd2b3 --- /dev/null +++ b/docs/api_extension.html.in @@ -0,0 +1,302 @@ + + + Implementing a new API in Libvirt + + + +

    Implementing a new API in Libvirt

    + +
      + +

      + 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. +

      + +

      + 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. +

      + +

      + 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: +

      + +

      + http://libvirt.org/downloads.html +

      + +

      + Once you have a working development environment, the steps to create a + new API are: +

      +
        +
      1. define the public API
      2. +
      3. define the internal driver API
      4. +
      5. implement the public API
      6. +
      7. define the wire protocol format
      8. +
      9. implement the RPC client
      10. +
      11. implement the server side dispatcher
      12. +
      13. implement the driver methods
      14. +
      15. add virsh support
      16. +
      + +

      + 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. +

      + +

      + 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. +

      + +

      + 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. +

      + +

      With that said, let's begin.

      + +

      Defining the public API

      + +

      The first task is to define the public API and add it to:

      + +

      include/libvirt/libvirt.h.in

      + +

      + 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. +

      + +

      Once you have defined the API, you have to add the symbol names to:

      + +

      src/libvirt_public.syms

      + +

      See 0001-Step-1-of-8-Define-the-public-API.patch for example code.

      + + +

      Defining the internal API

      + +

      + 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. +

      + +

      + 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. +

      + +

      The driver structs are defined in:

      + +

      src/driver.h

      + +

      + To define the internal API, first typedef the driver function + prototype and then add a new field for it to the relevant driver + struct. +

      + +

      See 0002-Step-2-of-8-Define-the-internal-driver-API.patch

      + +

      Implementing the public API

      + +

      + 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: +

      + +
        +
      1. SHOULD log a message with VIR_DEBUG() indicating that it is + being called and its parameters;
      2. +
      3. MUST call virResetLastError();
      4. +
      5. SHOULD confirm that the connection is valid with + VIR_IS_CONNECT(conn);
      6. +
      7. 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;
      8. +
      9. SHOULD do basic validation of the parameters that are being + passed in;
      10. +
      11. MUST confirm that the driver for this connection exists and that + it implements this function;
      12. +
      13. MUST call the internal API;
      14. +
      15. SHOULD log a message with VIR_DEBUG() indicating that it is + returning, its return value, and status.
      16. +
      17. MUST return status to the caller.
      18. +
      + +

      The public API calls are implemented in:

      + +

      src/libvirt.c

      + +

      See 0003-Step-3-of-8-Implement-the-public-API.patch

      + + +

      Defining the wire protocol format

      + +

      + 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: +

      + +

      qemud/remote_protocol.x

      + +

      + 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. +

      + +

      + Second, add values to the remote_procedure enum for each new function + added to the API. +

      + +

      See 0004-Step-4-of-8-Define-the-wire-protocol-format.patch

      + +

      + 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 +

      + + +

      Implement the RPC client

      + +

      + 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: +

      + +

      src/remote_internal.c

      + +

      Each remote method invocation does the following:

      + +
        +
      1. locks the remote driver;
      2. +
      3. sets up the method arguments;
      4. +
      5. invokes the remote function;
      6. +
      7. checks the return value, if necessary;
      8. +
      9. extracts any returned data;
      10. +
      11. frees any returned data;
      12. +
      13. unlocks the remote driver.
      14. +
      + +

      + Once you have created the remote method calls, you have to add fields + for them to the driver structs for the appropriate remote driver. +

      + +

      See 0005-Step-5-of-8-Implement-the-RPC-client.patch

      + +

      Implement the server side dispatcher

      + +

      + 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: +

      + +

      qemud/remote.c

      + +

      Again, this step uses the .h files generated by make rpcgen.

      + +

      See 0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch

      + + +

      Implement the driver methods

      + +

      + 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. +

      + +

      + 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. +

      + +

      + 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. +

      + +

      See 0007-Step-7-of-8-Implement-the-driver-methods.patch

      + +

      Implement virsh commands

      + +

      + 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. +

      + +

      + 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. +

      + +

      See 0008-Step-8-of-8-Add-virsh-support.patch

      + +

      Once you have working functionality, run make check and make + syntax-check before generating patches.

      + + 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 index 0000000000..6d0cd68fd1 --- /dev/null +++ b/docs/api_extension/0001-Step-1-of-8-Define-the-public-API.patch @@ -0,0 +1,45 @@ +From 2ae8fd62a1e5e085b7902da9bc207b806d84fd91 Mon Sep 17 00:00:00 2001 +From: David Allan +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 index 0000000000..231cbdffad --- /dev/null +++ b/docs/api_extension/0002-Step-2-of-8-Define-the-internal-driver-API.patch @@ -0,0 +1,37 @@ +From b26d7fc2d64e7e6e4d3ea2b43361015d3620d7a6 Mon Sep 17 00:00:00 2001 +From: David Allan +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 index 0000000000..079bd06b39 --- /dev/null +++ b/docs/api_extension/0003-Step-3-of-8-Implement-the-public-API.patch @@ -0,0 +1,120 @@ +From fc585594a207dfb9149e7d3d01c9eb1c79b6d52d Mon Sep 17 00:00:00 2001 +From: David Allan +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 index 0000000000..899026302b --- /dev/null +++ b/docs/api_extension/0004-Step-4-of-8-Define-the-wire-protocol-format.patch @@ -0,0 +1,48 @@ +From bce8f1243b0454c0d70e3db832a039d22faab09a Mon Sep 17 00:00:00 2001 +From: David Allan +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 index 0000000000..6f87dea7f9 --- /dev/null +++ b/docs/api_extension/0005-Step-5-of-8-Implement-the-RPC-client.patch @@ -0,0 +1,85 @@ +From ff272552c297966ace3492aefe91fc830152251a Mon Sep 17 00:00:00 2001 +From: David Allan +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 index 0000000000..96df45323b --- /dev/null +++ b/docs/api_extension/0006-Step-6-of-8-Implement-the-server-side-dispatcher.patch @@ -0,0 +1,71 @@ +From 4c5166df583459574526841234d61d6ae5be19a0 Mon Sep 17 00:00:00 2001 +From: David Allan +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 index 0000000000..ddd0a41614 --- /dev/null +++ b/docs/api_extension/0007-Step-7-of-8-Implement-the-driver-methods.patch @@ -0,0 +1,1172 @@ +From 04d20a662109de6727232eb1213627877bb9662f Mon Sep 17 00:00:00 2001 +From: David Allan +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 + #include ++#include ++#include + + #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, " %d\n", + data->scsi_host.host); ++ if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { ++ virBufferAddLit(&buf, " \n"); ++ virBufferVSprintf(&buf, ++ " %s\n", data->scsi_host.wwnn); ++ virBufferVSprintf(&buf, ++ " %s\n", data->scsi_host.wwpn); ++ virBufferAddLit(&buf, " \n"); ++ } ++ if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) { ++ virBufferAddLit(&buf, " \n"); ++ } ++ + break; + case VIR_NODE_DEV_CAP_SCSI: + virBufferVSprintf(&buf, " %d\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 + + #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 ++ ++#include ++ ++#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 index 0000000000..55b758b820 --- /dev/null +++ b/docs/api_extension/0008-Step-8-of-8-Add-virsh-support.patch @@ -0,0 +1,133 @@ +From 193cc4abbb6c2fc5557d3699f86ff0103d5a21ef Mon Sep 17 00:00:00 2001 +From: David Allan +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 + diff --git a/docs/archdomain.html b/docs/archdomain.html index a404c25d43..09c8c5bb6e 100644 --- a/docs/archdomain.html +++ b/docs/archdomain.html @@ -88,6 +88,10 @@ +
    • +
      + Internals +
    • diff --git a/docs/archnetwork.html b/docs/archnetwork.html index 6f4f8c1a37..d01c1d670e 100644 --- a/docs/archnetwork.html +++ b/docs/archnetwork.html @@ -88,6 +88,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/archnode.html b/docs/archnode.html index d2e4cdfcf0..d5b6d303ce 100644 --- a/docs/archnode.html +++ b/docs/archnode.html @@ -88,6 +88,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/archstorage.html b/docs/archstorage.html index cc33db8b59..5d08da87bc 100644 --- a/docs/archstorage.html +++ b/docs/archstorage.html @@ -88,6 +88,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/auth.html b/docs/auth.html index 590093e66c..dd16d5cc5c 100644 --- a/docs/auth.html +++ b/docs/auth.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/bindings.html b/docs/bindings.html index e77846cd5b..8196a42e0d 100644 --- a/docs/bindings.html +++ b/docs/bindings.html @@ -72,6 +72,10 @@
    • +
    • +
      + Internals +
    • diff --git a/docs/deployment.html b/docs/deployment.html index 36cfbc7e90..da1423cac1 100644 --- a/docs/deployment.html +++ b/docs/deployment.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/docs.html b/docs/docs.html index f6eb610978..49a62d54d1 100644 --- a/docs/docs.html +++ b/docs/docs.html @@ -63,6 +63,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drivers.html b/docs/drivers.html index 27328883f6..e535ea7ea9 100644 --- a/docs/drivers.html +++ b/docs/drivers.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvlxc.html b/docs/drvlxc.html index 0f021fad3e..a8a4073b00 100644 --- a/docs/drvlxc.html +++ b/docs/drvlxc.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvopenvz.html b/docs/drvopenvz.html index ac932dd481..579fdf0a42 100644 --- a/docs/drvopenvz.html +++ b/docs/drvopenvz.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvqemu.html b/docs/drvqemu.html index 20fff47040..6f1e271d0e 100644 --- a/docs/drvqemu.html +++ b/docs/drvqemu.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvremote.html b/docs/drvremote.html index 5848eb29b1..445f085959 100644 --- a/docs/drvremote.html +++ b/docs/drvremote.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvtest.html b/docs/drvtest.html index 0b4b9dc10e..efcfec3f4d 100644 --- a/docs/drvtest.html +++ b/docs/drvtest.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvuml.html b/docs/drvuml.html index 6b66ac7896..f4c187ed1b 100644 --- a/docs/drvuml.html +++ b/docs/drvuml.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvvbox.html b/docs/drvvbox.html index bdc3d3bbac..be6ff97713 100644 --- a/docs/drvvbox.html +++ b/docs/drvvbox.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/drvxen.html b/docs/drvxen.html index 49449617c8..62327884db 100644 --- a/docs/drvxen.html +++ b/docs/drvxen.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/format.html b/docs/format.html index 624c8604df..e97d0e7475 100644 --- a/docs/format.html +++ b/docs/format.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/formatcaps.html b/docs/formatcaps.html index e27bacd3f7..5b20aacfe8 100644 --- a/docs/formatcaps.html +++ b/docs/formatcaps.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/formatdomain.html b/docs/formatdomain.html index 27e42acb17..e13cd690f8 100644 --- a/docs/formatdomain.html +++ b/docs/formatdomain.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/formatnetwork.html b/docs/formatnetwork.html index 59539c2af7..0b25a0be11 100644 --- a/docs/formatnetwork.html +++ b/docs/formatnetwork.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/formatnode.html b/docs/formatnode.html index 1593b69f0f..4d30b0c1d7 100644 --- a/docs/formatnode.html +++ b/docs/formatnode.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/formatstorage.html b/docs/formatstorage.html index e96712db3d..91e63b4c33 100644 --- a/docs/formatstorage.html +++ b/docs/formatstorage.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/goals.html b/docs/goals.html index 5e24327f2a..b25b0d3334 100644 --- a/docs/goals.html +++ b/docs/goals.html @@ -88,6 +88,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/hvsupport.html b/docs/hvsupport.html index 5f903f8324..3e4ddb10f9 100644 --- a/docs/hvsupport.html +++ b/docs/hvsupport.html @@ -76,6 +76,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/internals.html b/docs/internals.html new file mode 100644 index 0000000000..8756214117 --- /dev/null +++ b/docs/internals.html @@ -0,0 +1,117 @@ + + + + + + + + + libvirt: libvirt internals + + + + +
      + +
      +

      libvirt internals

      +

      + 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. +

      +
      +
      + + + diff --git a/docs/internals.html.in b/docs/internals.html.in new file mode 100644 index 0000000000..d39098e20a --- /dev/null +++ b/docs/internals.html.in @@ -0,0 +1,11 @@ + + +

      libvirt internals

      + +

      + 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. +

      + + diff --git a/docs/intro.html b/docs/intro.html index c02e5bd985..524f73dd4a 100644 --- a/docs/intro.html +++ b/docs/intro.html @@ -88,6 +88,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/java.html b/docs/java.html index 572e583193..c1b4f65e88 100644 --- a/docs/java.html +++ b/docs/java.html @@ -72,6 +72,10 @@
    • +
    • +
      + Internals +
    • diff --git a/docs/logging.html b/docs/logging.html index 167fb77710..ea00efab48 100644 --- a/docs/logging.html +++ b/docs/logging.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/python.html b/docs/python.html index b4f57a14e8..e44e8c7d25 100644 --- a/docs/python.html +++ b/docs/python.html @@ -72,6 +72,10 @@
    • +
    • +
      + Internals +
    • diff --git a/docs/remote.html b/docs/remote.html index 391315677b..30750bc980 100644 --- a/docs/remote.html +++ b/docs/remote.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/sitemap.html b/docs/sitemap.html index d03c64a005..a6591e5e3b 100644 --- a/docs/sitemap.html +++ b/docs/sitemap.html @@ -220,6 +220,12 @@
    • Java overview of the Java API bindings +
    • + Internals + Working on the internals of libvirt API, driver and daemon code +
    • Wiki User contributed content diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in index 00328e1714..2e21530c04 100644 --- a/docs/sitemap.html.in +++ b/docs/sitemap.html.in @@ -210,6 +210,16 @@
    • +
    • + Internals + Working on the internals of libvirt API, driver and daemon code + +
    • diff --git a/docs/storage.html b/docs/storage.html index 94a8621faa..a4fb720264 100644 --- a/docs/storage.html +++ b/docs/storage.html @@ -100,6 +100,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/uri.html b/docs/uri.html index a663aad13f..ee25bfc86d 100644 --- a/docs/uri.html +++ b/docs/uri.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +
    • diff --git a/docs/windows.html b/docs/windows.html index 7e9745eda2..39dd1f6ed6 100644 --- a/docs/windows.html +++ b/docs/windows.html @@ -84,6 +84,10 @@
      Language bindings
      +
    • +
      + Internals +