On 06/18/2010 03:11 PM, Eduardo Otubo wrote:
Hello Folks,
This is the result of a couple of months of hard work. I added the storage
management driver to the Power Hypervisor driver. This is a big but simple
patch, it's just a new set of functions, nothing more. I could split it
into multiple commits, but the feature freeze starts in some hours and I
really reed this feature to be included in the next release.
This patch includes:
* Storage driver: The set of pool-* and vol-* functions.
* attach-disk function.
* Support for IVM on the new functions.
I've been looking at this code for a long time, so I apologize now for the
silly mistakes that might be present. Looking forward to see the comments.
Thanks!
Yes, it's quite involved, but it's all code addition restricted to just
the phyp backend, so a single commit is acceptable to me. However,
breaking it into smaller commits may help getting a particular commit
through the review process faster (since most of your APIs don't depend
on each other, it would be reasonable to break it into 3-4 driver
additions per patch, rather than all 15 new driver callbacks in one go).
---
src/phyp/phyp_driver.c | 1638 +++++++++++++++++++++++++++++++++++++++++++++++-
src/phyp/phyp_driver.h | 52 ++
2 files changed, 1688 insertions(+), 2 deletions(-)
diff --git a/src/phyp/phyp_driver.h b/src/phyp/phyp_driver.h
It's a shame that git lists files alphabetically, because I like to
review the headers first. I've reordered my reply accordingly ;)
index 80ff0c3..2606fe4 100644
--- a/src/phyp/phyp_driver.h
+++ b/src/phyp/phyp_driver.h
@@ -75,6 +75,58 @@ struct _phyp_driver {
char *managed_system;
};
+
+/*
+ * Storage functions
+ * */
+virStorageVolPtr
+phypStorageVolCreateXML(virStoragePoolPtr pool, const char *xmldesc,
+ unsigned int flags ATTRIBUTE_UNUSED);
Do these new functions really need to be exported (by modifying
src/libvirt_private.syms or some such)? I'd almost rather see them just
declared private within phyp_driver.h, since the only time they will be
invoked outside of phyp_driver.c is via the driver context.
I only found two instances of the string phypStorageVolCreateXML in your
patch - this declaration, and its implementation. But no one calls it,
and you haven't yet registered it in a driver callback array. Did you
forget to set .volCreateXML properly in your new virStorageDriver?
+virStoragePoolPtr phypStoragePoolCreateXML(virConnectPtr conn,
+ const char *xml,
+ unsigned int flags
+ ATTRIBUTE_UNUSED);
Technically, new code should probably not be using ATTRIBUTE_UNUSED on
flags; more on that at the implementation.
+char * phypGetStoragePoolXMLDesc(virStoragePoolPtr pool, unsigned
int
flags);
Formatting nit: no space between the * and the function name.
diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c
index cefb8be..77a74ef 100644
--- a/src/phyp/phyp_driver.c
+++ b/src/phyp/phyp_driver.c
@@ -56,6 +56,7 @@
#include "virterror_internal.h"
#include "uuid.h"
#include "domain_conf.h"
+#include "storage_conf.h"
#include "nodeinfo.h"
#include "phyp_driver.h"
@@ -1680,6 +1681,466 @@ phypDomainSetCPU(virDomainPtr dom, unsigned int nvcpus)
}
+static char *
+phypGetLparProfile(virConnectPtr conn, int lpar_id)
+{
+ ConnectionData *connection_data = conn->networkPrivateData;
+ phyp_driverPtr phyp_driver = conn->privateData;
+ LIBSSH2_SESSION *session = connection_data->session;
+ char *managed_system = phyp_driver->managed_system;
+ int system_type = phyp_driver->system_type;
+ int exit_status = 0;
+ char *cmd = NULL;
+ char *ret = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ virBufferAddLit(&buf, "lssyscfg");
+ if (system_type == HMC)
+ virBufferVSprintf(&buf, " -m %s ", managed_system);
+ virBufferVSprintf(&buf,
+ " -r prof --filter lpar_ids=%d -F name|head -n 1",
+ lpar_id);
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return NULL;
+ }
+ cmd = virBufferContentAndReset(&buf);
+
+ ret = phypExec(session, cmd, &exit_status, conn);
+
+ if (exit_status < 0 || ret == NULL)
+ goto err;
+
+ char *char_ptr = strchr(ret, '\n');
+
+ if (char_ptr)
+ *char_ptr = '\0';
+
Hmm. The 'head -n 1' you appended to cmd will guarantee that there will
be at most one newline, and if present, it will be the last byte in the
returned string. Meanwhile, some older systems didn't support 'head -n
1' (even though POSIX now requires it). How much more output does
lssyscfg typically produce? In other words, are we saving ourselves
some malloc overhead by trimming the stdout via head, or is it just
simpler to avoid the portability hassle, and skip piping the results
through head, since this strchr does the same thing anyway?
+static int
+phypGetVIOSNextSlotNumber(virConnectPtr conn)
+{
+ ConnectionData *connection_data = conn->networkPrivateData;
+ phyp_driverPtr phyp_driver = conn->privateData;
+ LIBSSH2_SESSION *session = connection_data->session;
+ char *managed_system = phyp_driver->managed_system;
+ int system_type = phyp_driver->system_type;
+ int vios_id = phyp_driver->vios_id;
+ int exit_status = 0;
+ char *char_ptr;
+ char *cmd = NULL;
+ char *ret = NULL;
+ char *profile = NULL;
+ int slot = 0;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (!(profile = phypGetLparProfile(conn, vios_id))) {
+ VIR_ERROR("%s", "Unable to get VIOS profile name.");
+ goto err;
+ }
+
+ virBufferAddLit(&buf, "echo $((`lssyscfg");
$(()) is another construct guaranteed by POSIX but not portable to all
current /bin/sh. Are we assured that phyp can only run on systems with
decent /bin/sh, or should we be worried about portability to Solaris (in
which case, use expr instead of $(()).)
+ if (system_type == HMC)
+ virBufferVSprintf(&buf, " -m %s ", managed_system);
+ virBufferVSprintf(&buf, "-r prof --filter "
+ "profile_names=%s -F virtual_eth_adapters,"
+ "virtual_opti_pool_id,virtual_scsi_adapters,"
+ "virtual_serial_adapters|sed -e 's/\"//g' -e
"
+ "'s/,/\\n/g'|sed -e
's/\\(^[0-9][0-9]\\*\\).*$/\\1/'"
+ "|sort|tail -n 1` +1 ))", profile);
Ouch. If the result of the `` command substitution begins with 0, you
have a problem with octal numbers. Remember, $((010 + 1)) is not the
same as $((10 + 1)). Perhaps you can modify the sed commands used in
your script to strip leading 0?
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return -1;
+ }
+ cmd = virBufferContentAndReset(&buf);
+
+ ret = phypExec(session, cmd, &exit_status, conn);
+
+ if (exit_status < 0 || ret == NULL)
+ goto err;
+
+ if (virStrToLong_i(ret, &char_ptr, 10, &slot) == -1)
+ goto err;
And, rather than doing $(()) (or expr) to do +1 in the shell, where you
have to worry about octal in the first place, why not just output the
last value as-is (that is, drop "echo $((`" and "` +1 ))" from cmd),
then do +1 in C code?
+static int
+phypCreateServerSCSIAdapter(virConnectPtr conn)
+{
...
+ virBufferVSprintf(&buf, "-r prof --filter
lpar_ids=%d,profile_names=%s"
+ " -F virtual_scsi_adapters|sed -e s/\"//g",
Won't work. In addition to escaping " for inclusion in a C string, you
also need to quote it from unbalanced use in shell code. In other
words, you want to execute
...|sed -e 's/"//g'
which requires a C string of:
"...|sed -e 's/\"//g'"
or you want to execute
...|sed -e s/\"//g
which requires a C string of:
"...|sed -e s/\\\"//g"
+static char *
+phypGetVIOSFreeSCSIAdapter(virConnectPtr conn)
+{
+ ConnectionData *connection_data = conn->networkPrivateData;
+ phyp_driverPtr phyp_driver = conn->privateData;
+ LIBSSH2_SESSION *session = connection_data->session;
+ char *managed_system = phyp_driver->managed_system;
+ int system_type = phyp_driver->system_type;
+ int vios_id = phyp_driver->vios_id;
+ int exit_status = 0;
+ char *cmd = NULL;
+ char *ret = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (system_type == HMC) {
+ virBufferVSprintf(&buf, "viosvrcmd -m %s --id %d -c ",
+ managed_system, vios_id);
+ virBufferVSprintf(&buf, "'lsmap -all -field svsa backing -fmt
,'");
+ } else {
+ virBufferVSprintf(&buf, "lsmap -all -field svsa backing -fmt ,");
+ }
+ virBufferVSprintf(&buf, "|grep -v ',[^.*]'|head -n 1|sed -e
's/,//g'");
Here, you can save a couple of processes, and avoid any portability
problems with 'head -n 1' at the same time.
...|grep -v ',[^.*]'|head -n 1|sed -e 's/,//g'
says to exclude lines that contain ,. or ,*, then print the first
remaining line with commas removed. But sed can do all of that:
...|sed -n '/,[^.*]/! {
s/,//g
p
q
}'
Yes, the embedded newlines are required for strict POSIX compatibility.
But, since this is not the critical path, and not everyone is a sed
wizard, I'm okay with the 3-process approach instead of the
do-it-all-in-one sed script.
+int
+phypAttachDevice(virDomainPtr domain, const char *xml)
+{
...
+ if (!
+ (vios_name =
+ phypGetLparNAME(session, managed_system, vios_id, conn))) {
+ VIR_ERROR("%s", "Unable to get VIOS name");
Here, you could use VIR_ERROR0(str) instead of VIR_ERROR("%s", str),
since your string has no % symbols. But you do need to translate the
string (with _() markings),
...
+ if (system_type == HMC) {
+ virBufferVSprintf(&buf, "viosvrcmd -m %s --id %d -c ",
+ managed_system, vios_id);
+ virBufferVSprintf(&buf, "'mkvdev -vdev %s -vadapter %s'",
+ dev->data.disk->src, scsi_adapter);
+ } else {
+ virBufferVSprintf(&buf, "mkvdev -vdev %s -vadapter %s",
+ dev->data.disk->src, scsi_adapter);
+ }
Here's some ideas for sharing more of the code (making it easier to
adjust the command in just one place, if you find down the road that
mkvdev needs an argument change):
char *end = "";
if (system_type == HMC) {
virBufferVSprintf(&buf, "viosvrcmd ... -c '", ...);
end = "'";
}
virBufferVSprintf(&buf, "mkvdev -vdev %s -vadapter %s%s",
dev->data.disk->src, scsi_adapter, end);
OR
if (system_type == HMC)
virBufferVSprintf(&buf, "viosvrcmd ... -c '", ...);
virBufferVSprintf(&buf, "mkvdev ... -vadapter %s", ... scsi_adapter);
if (system_type == HMC)
virBufferAddChar(&buf, '\'');
+ if (!(profile = phypGetLparProfile(conn, domain->id))) {
+ VIR_ERROR("%s", "Unable to get VIOS profile name.");
+ goto err;
+ }
Another case of a missing string translation. How come 'make
syntax-check' isn't catching it?
+ virBufferVSprintf(&buf,
+ "-r prof -i 'name=%s,lpar_id=%d,"
+
"\"virtual_scsi_adapters=%s,%d/client/%d/%s/0\"'",
+ domain->name, domain->id, ret, slot,
Since we're passing a lot of things through shell, are you sure that
domain->name (and all the other strings that you substitute via %s) will
never contain ", $, or any other character special to shell processing
that would throw your cmd for a loop? I know we have various XML schema
requirements, but I haven't checked whether a domain name is something
that libvirt has already guaranteed to be restricted to a reasonable
subset (alphanumeric, ., _, -) or if it allows absolute freedom in
naming with consequences to everyone else dealing with arbitrary names.
+static int
+phypVolumeGetKey(virConnectPtr conn, char *key, const char *name)
...
+ if (system_type == HMC) {
+ virBufferVSprintf(&buf, "viosvrcmd -m %s --id %d -c ",
+ managed_system, vios_id);
+ virBufferVSprintf(&buf, "'lslv %s -field lvid'", name);
+ } else {
+ virBufferVSprintf(&buf, "lslv %s -field lvid", name);
Another example where you could simplify to one "lslv ..." string with a
little bit of refactoring.
+ }
+ virBufferVSprintf(&buf, "|sed -e 's/^LV IDENTIFIER://' -e 's/\\
//g'");
+
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return -1;
+ }
+ cmd = virBufferContentAndReset(&buf);
+
+ ret = phypExec(session, cmd, &exit_status, conn);
+
+ if (exit_status < 0 || ret == NULL)
+ goto err;
+
+ char *char_ptr = strchr(ret, '\n');
+
+ if (char_ptr)
+ *char_ptr = '\0';
+
+ if (memmove(key, ret, PATH_MAX) == NULL)
key and ret don't overlap (since ret was freshly malloc'd). Use memcpy
instead for speed.
+static char *
+phypGetStoragePoolDevice(virConnectPtr conn, char *name)
+{
+ ConnectionData *connection_data = conn->networkPrivateData;
+ phyp_driverPtr phyp_driver = conn->privateData;
+ LIBSSH2_SESSION *session = connection_data->session;
+ char *managed_system = phyp_driver->managed_system;
+ int system_type = phyp_driver->system_type;
+ int vios_id = phyp_driver->vios_id;
+ int exit_status = 0;
+ char *cmd = NULL;
+ char *ret = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (system_type == HMC) {
+ virBufferVSprintf(&buf, "viosvrcmd -m %s --id %d -c ",
+ managed_system, vios_id);
+ virBufferVSprintf(&buf, "'lssp -detail -sp %s -field
name'", name);
+ } else {
+ virBufferVSprintf(&buf, "lssp -detail -sp %s -field name", name);
+ }
+ virBufferVSprintf(&buf, "|sed '1d'|sed -e 's/\\
//g'");
Two sed processes:
...|sed '1d'|sed -e 's/\\ //g'
can be simplified to one:
...|sed '1d; s/\\ //g'
Also, '\ ' is not a portable sed escape sequence. Did you mean to use
the C string "s/\\\\ //g" for the shell line 's/\\ //g', in order to
delete backslash-space sequences from the output? (multiple instances
of this pattern in your patch)
+static int
+phypBuildVolume(virConnectPtr conn, const char *lvname, const char *spname,
+ unsigned int capacity, char *key)
+{
...
+ if (exit_status < 0) {
+ VIR_ERROR("%s\"%s\"", "Unable to create Volume. Reason:
", ret);
Missing translation, and unusual capititalization in the middle of the
sentence. I'd just use:
VIR_ERROR(_("unable to create volume: %s"), ret);
(GNU Coding Standards demand starting with lower case; however libvirt
is not a GNU project, so we aren't bound by that rule. Right now, we
aren't very consistent yet on whether error messages start with upper or
lower case, so although I use lower, I won't reject a patch that uses
upper.)
+virStorageVolPtr
+phypStorageVolCreateXML(virStoragePoolPtr pool, const char *xml,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+
+ virStorageVolDefPtr voldef = NULL;
+ virStoragePoolDefPtr spdef = NULL;
+ virStorageVolPtr vol = NULL;
+ char *key = NULL;
I'm wary of any new code that declares flags with ATTRIBUTE_UNUSED - it
means we aren't checking for unknown flags, which makes it harder to
actually define a new flag down the road. To get around that, you
should be using:
virCheckFlags(0, NULL);
which checks that flags is explicitly 0.
+
+ if (VIR_ALLOC_N(key, PATH_MAX) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
Ouch. On GNU/Hurd, PATH_MAX is unlimited, so it is not a size that you
can safely malloc. Is there a better bound for the maximum key size
that you expect, even if that means adding #define MAX_KEY_SIZE
(1024*4), or whatever value is a better reasonable limit? At any rate,
there's a reason that 'git grep "ALLOC.*PATH_MAX"' returns no hits,
so
this needs adjusting.
+
+ /* Filling spdef manually
+ * */
+ if (pool->name != NULL) {
+ spdef->name = pool->name;
+ } else {
+ VIR_ERROR("%s", "Unable to determine storage pool's
name.");
+ goto err;
+ }
+
+ if (memmove(spdef->uuid, pool->uuid, VIR_UUID_BUFLEN) == NULL) {
Again, spdef and pool don't overlap, so you should use memcpy().
+ if ((voldef = virStorageVolDefParseString(spdef, xml)) == NULL)
{
+ VIR_ERROR("%s", "Error parsing volume XML.");
+ goto err;
+ }
+
+ /* checking if this name already exists on this system */
+ if (phypVolumeLookupByName(pool, voldef->name) != NULL) {
+ VIR_ERROR("%s", "StoragePool name already exists.");
More strings to mark for translation (I'll quit commenting on this for
this round of review, although there's probably more instances of it
throughout the patch).
+virStorageVolPtr
+phypVolumeLookupByPath(virConnectPtr conn, const char *volname)
+{
+ ConnectionData *connection_data = conn->networkPrivateData;
+ phyp_driverPtr phyp_driver = conn->privateData;
+ LIBSSH2_SESSION *session = connection_data->session;
+ char *managed_system = phyp_driver->managed_system;
+ int system_type = phyp_driver->system_type;
+ int vios_id = phyp_driver->vios_id;
+ int exit_status = 0;
+ char *cmd = NULL;
+ char *spname = NULL;
+ char *key = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (system_type == HMC) {
+ virBufferVSprintf(&buf, "viosvrcmd -m %s --id %d -c ",
+ managed_system, vios_id);
+ virBufferVSprintf(&buf, "'lslv %s -field vgname'",
volname);
+ } else {
+ virBufferVSprintf(&buf, "lslv %s -field vgname", volname);
+ }
+ virBufferVSprintf(&buf,
+ "|sed -e 's/^VOLUME\\ GROUP://g' -e 's/\\
//g'");
Another instance of a non-portable sed escape '\ '. But this time, I
see enough context that it looks like you are trying to just delete
spaces (and not backslash-space sequences). In which case, use either:
shell: ...|sed -e s/\^VOLUME\ GROUP://g -e...
C-string: "...|sed -e s/\\^VOLUME\\ GROUP://g -e..."
or
shell: ...|sed -e 's/^VOLUME GROUP://g' -e...
C-string: "...|sed -e '/^VOLUME GROUP://g' -e..."
+char *
+phypVolumeGetXMLDesc(virStorageVolPtr vol,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ virStorageVolDef voldef;
+ memset(&voldef, 0, sizeof(virStorageVolDef));
Another place for virCheckFlags(0, NULL)
+
+ if (memmove(pool.uuid, sp->uuid, VIR_UUID_BUFLEN) == NULL) {
memcpy
+
+virStorageVolPtr
+phypVolumeLookupByName(virStoragePoolPtr pool, const char *volname)
+{
+
+ char key[PATH_MAX];
Stack-allocating PATH_MAX bytes is just as non-portable as malloc'ing
that many bytes; but here, you've got precedence in existing libvirt
source (ultimately, I mean to sweep the source code to clean that all
up). Are you sure we don't have a better bound on the maximum key
length? And actually, looking at src/datatypes.c, the const char *key
argument is a bit of a misnomer; the docs call it uuid, and we DO have a
fixed bound for UUID length, which is much smaller than PATH_MAX (we
also have VIR_UUID_STRING_BUFLEN defined in libvirt.h).
+int
+phypStoragePoolListVolumes(virStoragePoolPtr pool, char **const volumes,
+ int nvolumes)
+{
...
+ virBufferVSprintf(&buf, "|sed '1d'|sed
'1d'");
Save a process:
...|sed '1d'|sed '1d'
is equivalent to:
...|sed 2d
+int
+phypStoragePoolNumOfVolumes(virStoragePoolPtr pool)
+{
...
+ virBufferVSprintf(&buf, "|sed '1d'|sed
'1d'|grep -c '^.*$'");
Here, rather than using sed '1d' twice,...
+
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return -1;
+ }
+ cmd = virBufferContentAndReset(&buf);
+
+ ret = phypExec(session, cmd, &exit_status, conn);
+
+ if (exit_status < 0 || ret == NULL)
+ goto err;
+
+ if (virStrToLong_i(ret, &char_ptr, 10, &nvolumes) == -1)
+ goto err;
...just subtract 2 from nvolumes here in C code.
+int
+phypDestroyStoragePool(virStoragePoolPtr pool)
+{
...
+ if (system_type == HMC) {
+ virBufferVSprintf(&buf, "viosvrcmd -m %s --id %d -c ",
+ managed_system, vios_id);
+ virBufferVSprintf(&buf, "'rmsp %s'", pool->name);
+ } else {
+ virBufferVSprintf(&buf, "'rmsp %s'", pool->name);
+ }
Looks like you typo'd the IVM line (left the '' in, so you will fail
trying to call the command "rmsp name" rather than the intended command
"rmsp" with argument "name"). Another reason why refactoring your
code
to share just a single rmsp C string will help avoid mistakes like this.
+virStoragePoolPtr
+phypStoragePoolCreateXML(virConnectPtr conn,
+ const char *xml,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+
+ virStoragePoolDefPtr def = NULL;
+ virStoragePoolPtr sp = NULL;
virCheckFlags(0, NULL) (won't comment on it any more for this round of
review...)
+virStoragePoolPtr
+phypGetStoragePoolLookUpByUUID(virConnectPtr conn,
+ const unsigned char *uuid)
+{
...
+ if (STREQLEN((char *) local_uuid, (char *) uuid,
VIR_UUID_BUFLEN)) {
Here, it is simpler to avoid the casts, by doing:
if (!memcmp(local_uuid, uuid, VIR_UUID_BUFLEN)) {
It also matches existing style in the rest of libvirt ('git grep
"STREQLEN.*VIR_UUID"' vs. 'git grep -i
"cmp.*VIR_UUID_BUFLEN"').
+int
+phypNumOfStoragePools(virConnectPtr conn)
+{
+ } else {
+ virBufferVSprintf(&buf, "lsvg");
+ }
+ virBufferVSprintf(&buf, "grep -c '^.*$'");
Missing a '|' before the grep.
+}
+virDrvOpenStatus
+phypStorageOpen(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED)
Add a newline between the closing } of one function and the start of
another.
Overall, my review was mainly focused on style and shell portability. I
don't have access to a phyp setup at the moment, so I can't really test
if your various command lines make sense (I'm assuming they do).
Towards the end, I kept on spotting (and ignoring) the same issues
again, so remember to do global searches when addressing a comment,
rather than just at the place where I made the comment. But in general,
this looks promising, and hopefully we can get things turned around fast
enough to decide whether this is worth including in libvirt 0.8.2.
--
Eric Blake eblake(a)redhat.com +1-801-349-2682
Libvirt virtualization library
http://libvirt.org