[iwhd] maint: ignore a few more generated files
by Jim Meyering
commit ebd52ad942e69927d97c344691a7655155490f5e
Author: Jim Meyering <meyering(a)redhat.com>
Date: Tue Mar 15 12:32:50 2011 +0100
maint: ignore a few more generated files
.gitignore | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f5ef3e9..700fa70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
/gnulib-tests
/iwhd
/iwhd-*.tar.gz
+/iwhd-*.tar.xz
/iwhd-conf.js
/iwhd-qparser.c
/iwhd-qparser.h
@@ -30,6 +31,9 @@
/t/parser
/t/parser.c
ChangeLog
+ID
+INSTALL
Makefile
Makefile.in
+TAGS
\#*\#
13 years, 1 month
[iwhd] fix remaining URLs in attribute-listing output to be accurate
by Jim Meyering
commit 6b9effc38341bfeaab330da9cd05917e67aad76c
Author: Jim Meyering <meyering(a)redhat.com>
Date: Tue Mar 15 11:37:44 2011 +0100
fix remaining URLs in attribute-listing output to be accurate
* template.c: Adjust the URLs we emit for the body and for each
individual attribute so that they actually work.
* doc/warehouse_api_final.md: Adjust examples to match.
* t/basic: Adjust test to expect new output.
doc/warehouse_api_final.md | 6 +++---
t/basic | 6 +++---
template.c | 4 ++--
3 files changed, 8 insertions(+), 8 deletions(-)
---
diff --git a/doc/warehouse_api_final.md b/doc/warehouse_api_final.md
index 31adfe9..f58496b 100644
--- a/doc/warehouse_api_final.md
+++ b/doc/warehouse_api_final.md
@@ -139,9 +139,9 @@ elements as shown here:
$ curl http://fserver-1:9090/my_bucket/file1
<object>
- <object_body path="http://fserver-1:9090/my_bucket/file1/body"/>
+ <object_body path="http://fserver-1:9090/my_bucket/file1"/>
<object_attr_list path="http://fserver-1:9090/my_bucket/file1/_attrs"/>
- <object_attr name="xyz" path="http://fserver-1:9090/my_bucket/file1/attr_xyz"/>
+ <object_attr name="xyz" path="http://fserver-1:9090/my_bucket/file1/xyz"/>
</object>
The object body can be stored and retrieved using PUT and GET respectively,
@@ -163,7 +163,7 @@ To set both of these attributes at once:
Single-attribute operations are also supported. To fetch a
single attribute:
- $ curl http://fserver-1:9090/my_bucket/file1/attr_color
+ $ curl http://fserver-1:9090/my_bucket/file1/color
<attribute name="color">blue</attribute>
The attribute can also be set with a PUT to the same URL.
diff --git a/t/basic b/t/basic
index 010a1a7..fd4bca9 100644
--- a/t/basic
+++ b/t/basic
@@ -177,10 +177,10 @@ test "$(curl $bucket/attr_post/size)" = big || fail=1
cat <<EOF > exp-attr
<object>
- <object_body path="$bucket/attr_post/body"/>
+ <object_body path="$bucket/attr_post"/>
<object_attr_list path="$bucket/attr_post/_attrs"/>
- <object_attr name="shape" path="$bucket/attr_post/attr_shape"/>
- <object_attr name="size" path="$bucket/attr_post/attr_size"/>
+ <object_attr name="shape" path="$bucket/attr_post/shape"/>
+ <object_attr name="size" path="$bucket/attr_post/size"/>
</object>
EOF
# Print all attributes of an object:
diff --git a/template.c b/template.c
index f8fb27a..57e52ab 100644
--- a/template.c
+++ b/template.c
@@ -74,13 +74,13 @@ static const char xml_list_footer[] = "\
static const char xml_obj_header[] = "\
<object>\n\
- <object_body path=\"http://%s/%s/%s/body\"/>\n\
+ <object_body path=\"http://%s/%s/%s\"/>\n\
<object_attr_list path=\"http://%s/%s/%s/_attrs\"/>\
";
static const char xml_obj_entry[] = "\
\n\
- <object_attr name=\"%s\" path=\"http://%s/%s/%s/attr_%s\"/>\
+ <object_attr name=\"%s\" path=\"http://%s/%s/%s/%s\"/>\
";
static const char xml_obj_footer[] = "\
13 years, 1 month
[iwhd] doc: keep NEWS up to date
by Jim Meyering
commit 2863e1257cd3395865e52355d5ad9ad8b40bea3a
Author: Jim Meyering <meyering(a)redhat.com>
Date: Sat Mar 12 09:58:10 2011 +0100
doc: keep NEWS up to date
* NEWS: Mention changes.
NEWS | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+), 0 deletions(-)
---
diff --git a/NEWS b/NEWS
index 9a380f9..4ab8e6c 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,26 @@ iwhd NEWS -*- outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** New features
+
+ RHEV-M back-end
+
+** Bug fixes
+
+ attempting to delete a nonexistent bucket now elicits a 404 response
+ Before, it was not possible to distinguish empty and nonexistent buckets.
+
+ attribute lists are more useful (XML syntax fixes, URL fixes)
+
+** New APIs
+
+ GET http://host/B/O/_attrs now lists attributes of object O in bucket B.
+ Before, you would have had to use another, undocumented technique.
+
+** Infrastructure
+
+ include a fedora-style init script
+
* Noteworthy changes in release 0.91 (2011-02-10) [stable]
13 years, 1 month
3 commits - iwhd.init.in rest.c t/basic template.c template.h
by Jim Meyering
iwhd.init.in | 8 +--
rest.c | 119 +++++++++++++++++++++++++++++++++++++++--------------------
t/basic | 3 +
template.c | 45 ++++++++++++++--------
template.h | 3 -
5 files changed, 117 insertions(+), 61 deletions(-)
New commits:
commit 25ef441355c4033adbb36e2780cd5d1795e3458a
Author: Jim Meyering <meyering(a)redhat.com>
Date: Fri Mar 11 21:26:00 2011 +0100
move many decls "down" to where used; no semantic change
diff --git a/rest.c b/rest.c
index dfe3f76..4a74dde 100644
--- a/rest.c
+++ b/rest.c
@@ -200,7 +200,6 @@ proxy_get_cons (void *ctx, uint64_t pos, char *buf, size_t max)
pipe_shared *ps = pp->shared;
my_state *ms = ps->owner;
ssize_t done;
- void *child_res;
(void)pos;
@@ -232,7 +231,7 @@ proxy_get_cons (void *ctx, uint64_t pos, char *buf, size_t max)
}
if (done == (-1)) {
- child_res = NULL;
+ void *child_res = NULL;
pthread_join(ms->backend_th,&child_res);
if (child_res == THREAD_FAILED) {
DPRINTF("GET producer failed\n");
@@ -258,7 +257,6 @@ proxy_get_data (void *cctx, struct MHD_Connection *conn, const char *url,
pipe_private *pp2;
char *my_etag;
const char *user_etag;
- int rc;
(void)cctx;
(void)method;
@@ -331,7 +329,7 @@ proxy_get_data (void *cctx, struct MHD_Connection *conn, const char *url,
pp2 = NULL;
}
- rc = pipe_cons_wait_init(&ms->pipe);
+ int rc = pipe_cons_wait_init(&ms->pipe);
ms->rc = (rc == 0) ? MHD_HTTP_OK : MHD_HTTP_INTERNAL_SERVER_ERROR;
resp = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
@@ -353,8 +351,7 @@ proxy_get_data (void *cctx, struct MHD_Connection *conn, const char *url,
static void
recheck_replication (my_state * ms, char *policy)
{
- int rc;
- char fixed[MAX_FIELD_LEN];
+ int rc; /* FIXME: set but never used */
if (is_reserved(ms->key,reserved_name)) {
DPRINTF("declining to replicate reserved object %s\n",ms->key);
@@ -378,6 +375,7 @@ recheck_replication (my_state * ms, char *policy)
}
if (policy) {
+ char fixed[MAX_FIELD_LEN];
DPRINTF(" implementing policy %s\n",policy);
/*
* Can't use ms->url here because it might be a bucket POST
@@ -398,10 +396,7 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
{
struct MHD_Response *resp;
my_state *ms = *rctx;
- pipe_private *pp;
int rc;
- char *etag = NULL;
- void *child_res;
(void)cctx;
(void)method;
@@ -428,7 +423,7 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
}
ms->size = 0;
pipe_init_shared(&ms->pipe,ms,1);
- pp = pipe_init_private(&ms->pipe);
+ pipe_private *pp = pipe_init_private(&ms->pipe);
if (!pp) {
return MHD_NO;
}
@@ -484,6 +479,8 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
*data_size = 0;
}
else {
+ char *etag = NULL;
+ void *child_res;
pipe_prod_finish(&ms->pipe);
pthread_join(ms->backend_th,&child_res);
if (child_res == THREAD_FAILED) {
@@ -502,7 +499,7 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
}
else {
etag = meta_did_put(ms->bucket,ms->key,me,
- ms->size);
+ ms->size);
}
DPRINTF("rereplicate (obj PUT)\n");
recheck_replication(ms,NULL);
@@ -856,7 +853,6 @@ proxy_query (void *cctx, struct MHD_Connection *conn, const char *url,
{
struct MHD_Response *resp;
my_state *ms = *rctx;
- int rc;
(void)cctx;
(void)method;
@@ -898,7 +894,7 @@ proxy_query (void *cctx, struct MHD_Connection *conn, const char *url,
if (!ms->pipe.data_ptr) {
return MHD_NO;
}
- rc = proxy_query_init(ms, ms->pipe.data_ptr);
+ int rc = proxy_query_init(ms, ms->pipe.data_ptr);
if (rc != MHD_HTTP_OK) {
resp = MHD_create_response_from_data(0,NULL,
MHD_NO,MHD_NO);
@@ -927,7 +923,6 @@ proxy_list_objs (void *cctx, struct MHD_Connection *conn, const char *url,
{
my_state *ms = *rctx;
struct MHD_Response *resp;
- int rc;
(void)cctx;
(void)url;
@@ -936,7 +931,7 @@ proxy_list_objs (void *cctx, struct MHD_Connection *conn, const char *url,
(void)data;
(void)data_size;
- rc = proxy_query_init(ms, NULL);
+ int rc = proxy_query_init(ms, NULL);
if (rc != MHD_HTTP_OK) {
resp = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
MHD_queue_response(conn,rc,resp);
@@ -967,7 +962,6 @@ proxy_delete (void *cctx, struct MHD_Connection *conn, const char *url,
char *bucket;
char *key;
char *stctx = NULL;
- int rc;
(void)cctx;
(void)method;
@@ -980,8 +974,8 @@ proxy_delete (void *cctx, struct MHD_Connection *conn, const char *url,
provider_t *main_prov = get_main_provider();
ms->thunk.parent = ms;
ms->thunk.prov = main_prov;
- rc = ms->thunk.prov->func_tbl->delete_func(main_prov,
- ms->bucket,ms->key,url);
+ int rc = ms->thunk.prov->func_tbl->delete_func(main_prov,
+ ms->bucket,ms->key,url);
if (rc == MHD_HTTP_OK) {
copied_url = strdup(url);
assert (copied_url);
@@ -1202,7 +1196,6 @@ post_foreach (void *kvv, void *ms_v)
static int
create_bucket (char *name, my_state *ms)
{
- int rc;
if (is_reserved(name, reserved_name)
|| is_reserved(name, reserved_bucket_name)) {
@@ -1210,7 +1203,7 @@ create_bucket (char *name, my_state *ms)
}
provider_t *main_prov = get_main_provider();
- rc = main_prov->func_tbl->bcreate_func(main_prov,name);
+ int rc = main_prov->func_tbl->bcreate_func(main_prov,name);
if (rc == MHD_HTTP_OK) {
if (meta_set_value(name,"_default", "_policy","0") != 0) {
DPRINTF("default-policy " "create failed\n");
@@ -1240,7 +1233,6 @@ control_api_root (void *cctx, struct MHD_Connection *conn, const char *url,
{
struct MHD_Response *resp;
my_state *ms = *rctx;
- int rc;
char *op;
char buf[80];
int len;
@@ -1272,7 +1264,7 @@ control_api_root (void *cctx, struct MHD_Connection *conn, const char *url,
return MHD_YES;
}
- rc = MHD_HTTP_BAD_REQUEST;
+ int rc = MHD_HTTP_BAD_REQUEST;
op = kv_hash_lookup(ms->dict,"op");
if (op) {
@@ -1312,7 +1304,6 @@ proxy_bucket_post (void *cctx, struct MHD_Connection *conn, const char *url,
{
struct MHD_Response *resp;
my_state *ms = *rctx;
- int rc;
char *key;
(void)cctx;
@@ -1339,7 +1330,7 @@ proxy_bucket_post (void *cctx, struct MHD_Connection *conn, const char *url,
*data_size = 0;
}
else {
- rc = MHD_HTTP_BAD_REQUEST;
+ int rc = MHD_HTTP_BAD_REQUEST;
key = kv_hash_lookup(ms->dict,"_key");
if (key) {
strncpy(ms->key,key,MAX_FIELD_LEN-1);
@@ -1518,7 +1509,6 @@ proxy_object_post (void *cctx, struct MHD_Connection *conn, const char *url,
{
struct MHD_Response *resp;
my_state *ms = *rctx;
- int rc;
char *op;
(void)cctx;
@@ -1545,7 +1535,7 @@ proxy_object_post (void *cctx, struct MHD_Connection *conn, const char *url,
*data_size = 0;
}
else {
- rc = MHD_HTTP_BAD_REQUEST;
+ int rc = MHD_HTTP_BAD_REQUEST;
if (!kv_find_val(ms->dict,post_find,NULL)) {
op = kv_hash_lookup(ms->dict,"op");
if (op) {
@@ -2035,9 +2025,7 @@ proxy_create_bucket (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
- struct MHD_Response *resp;
my_state *ms = *rctx;
- int rc;
(void)cctx;
(void)method;
@@ -2047,9 +2035,10 @@ proxy_create_bucket (void *cctx, struct MHD_Connection *conn, const char *url,
(void)url;
/* curl -T moo.empty http://localhost:9090/_new by accident */
- rc = create_bucket(ms->bucket,ms);
+ int rc = create_bucket(ms->bucket,ms);
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
+ struct MHD_Response *resp
+ = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
if (!resp) {
fprintf(stderr,"MHD_crfd failed\n");
return MHD_NO;
commit 2b7b1b79af54eebdfbb51d80d7094521b20fbbd1
Author: Pete Zaitcev <zaitcev(a)redhat.com>
Date: Thu Mar 10 15:21:19 2011 -0700
Return 404 from non-existing bucket
Maros found that a GET on non-existing bucket returns an empty list
of objects, thus making it impossible to distinguish between a non-exsting
bucket and an empty bucket. This occurs because there is no object that
describes "bucket" as such in database.
However in reality we always have "special" objects, such as the
default replication policy. Therefore, we can distinguish buckets
that do not exist, and have no objects of any kind, and those that
are empty, but actually have "_default" object.
One small difficulty is that the HTTP code has to be known before
the data transfer begins. For this to work, this patch starts the
query before MicroHTTPd response is set up. If we find any keys,
including hidden ones, the bucket is considered existing. Then,
the just-found key is saved for the proxy_query_func to return.
If the query returns nothing, we return 404 to the user.
diff --git a/rest.c b/rest.c
index 84757e9..dfe3f76 100644
--- a/rest.c
+++ b/rest.c
@@ -722,16 +722,18 @@ query_iterator (void *ctx, enum MHD_ValueKind kind, const char *key,
return MHD_YES;
}
-/* MHD reader function during queries. Return -1 for EOF. */
-static ssize_t
-proxy_query_func (void *ctx, uint64_t pos, char *buf, size_t max)
+static int
+proxy_query_init (my_state *ms, const char *expr)
{
- my_state *ms = ctx;
size_t len;
char *bucket;
char *key;
- (void)pos;
+ ms->query = meta_query_new(ms->bucket,NULL,expr);
+ if (!ms->query) {
+ DPRINTF("failed query\n");
+ return MHD_HTTP_INTERNAL_SERVER_ERROR;
+ }
if (!ms->gen_ctx) {
const char *accept_hdr
@@ -739,23 +741,55 @@ proxy_query_func (void *ctx, uint64_t pos, char *buf, size_t max)
"Accept");
ms->gen_ctx = tmpl_get_ctx(accept_hdr);
if (!ms->gen_ctx) {
- return -1;
+ DPRINTF("failed context\n");
+ return MHD_HTTP_INTERNAL_SERVER_ERROR;
}
len = tmpl_list_header(ms->gen_ctx);
if (!len) {
- return -1;
+ DPRINTF("failed header\n");
+ return MHD_HTTP_INTERNAL_SERVER_ERROR;
}
+ }
+
+ if (!meta_query_next(ms->query,&bucket,&key)) {
+ return MHD_HTTP_NOT_FOUND;
+ }
+ if (is_reserved(key,reserved_name)) {
+ return MHD_HTTP_OK;
+ }
+ len = tmpl_list_entry(ms->gen_ctx,bucket,key);
+ if (!len) {
+ DPRINTF("failed key save\n");
+ return MHD_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ return MHD_HTTP_OK;
+}
+
+/* MHD reader function during queries. Return -1 for EOF. */
+static ssize_t
+proxy_query_func (void *ctx, uint64_t pos, char *buf, size_t max)
+{
+ my_state *ms = ctx;
+ size_t len;
+ char *bucket;
+ char *key;
+
+ (void)pos;
+
+ if (ms->gen_ctx == TMPL_CTX_DONE) {
+ return -1;
+ }
+ if (ms->gen_ctx->len) {
+ len = ms->gen_ctx->len;
if (len > max) {
len = max;
}
memcpy(buf,ms->gen_ctx->buf,len);
+ ms->gen_ctx->buf += len;
+ ms->gen_ctx->len -= len;
return len;
}
- if (ms->gen_ctx == TMPL_CTX_DONE) {
- return -1;
- }
-
for(;;) {
if (!meta_query_next(ms->query,&bucket,&key)) {
break;
@@ -771,6 +805,8 @@ proxy_query_func (void *ctx, uint64_t pos, char *buf, size_t max)
len = max;
}
memcpy(buf,ms->gen_ctx->buf,len);
+ ms->gen_ctx->buf += len;
+ ms->gen_ctx->len -= len;
return len;
}
@@ -820,6 +856,7 @@ proxy_query (void *cctx, struct MHD_Connection *conn, const char *url,
{
struct MHD_Response *resp;
my_state *ms = *rctx;
+ int rc;
(void)cctx;
(void)method;
@@ -861,7 +898,14 @@ proxy_query (void *cctx, struct MHD_Connection *conn, const char *url,
if (!ms->pipe.data_ptr) {
return MHD_NO;
}
- ms->query = meta_query_new(ms->bucket,NULL,ms->pipe.data_ptr);
+ rc = proxy_query_init(ms, ms->pipe.data_ptr);
+ if (rc != MHD_HTTP_OK) {
+ resp = MHD_create_response_from_data(0,NULL,
+ MHD_NO,MHD_NO);
+ MHD_queue_response(conn,rc,resp);
+ MHD_destroy_response(resp);
+ return MHD_YES;
+ }
resp = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
CB_BLOCK_SIZE, proxy_query_func, ms, simple_closer);
if (!resp) {
@@ -883,6 +927,7 @@ proxy_list_objs (void *cctx, struct MHD_Connection *conn, const char *url,
{
my_state *ms = *rctx;
struct MHD_Response *resp;
+ int rc;
(void)cctx;
(void)url;
@@ -891,8 +936,13 @@ proxy_list_objs (void *cctx, struct MHD_Connection *conn, const char *url,
(void)data;
(void)data_size;
- ms->query = meta_query_new((char *)ms->bucket,NULL,NULL);
-
+ rc = proxy_query_init(ms, NULL);
+ if (rc != MHD_HTTP_OK) {
+ resp = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
+ MHD_queue_response(conn,rc,resp);
+ MHD_destroy_response(resp);
+ return MHD_YES;
+ }
resp = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
CB_BLOCK_SIZE, proxy_query_func, ms, simple_closer);
if (!resp) {
diff --git a/t/basic b/t/basic
index e524909..010a1a7 100644
--- a/t/basic
+++ b/t/basic
@@ -88,6 +88,9 @@ test "$(cat FS/b1/obj_test)" = foo || fail=1
curl $bucket/obj_test > f1copy
test "$(cat f1copy)" = foo || fail=1
+# Test that non-existing bucket gives 404 and not 500 or even 200 as before.
+curl -f http://localhost:$port/no_bucket 2> no_bucket.err
+grep ' 404$' no_bucket.err || fail=1
# Get root; default, then Accept: XML & JSON:
curl http://localhost:$port > root || fail=1
diff --git a/template.c b/template.c
index 4e2f315..f8fb27a 100644
--- a/template.c
+++ b/template.c
@@ -209,6 +209,7 @@ tmpl_get_ctx (const char *type)
tmp->format = &xml_format;
}
tmp->index = 0;
+ tmp->len = 0;
}
return tmp;
}
@@ -225,6 +226,7 @@ tmpl_root_header (tmpl_ctx_t *ctx, const char *name, const char *version)
return 0;
}
ctx->buf = ctx->raw_buf;
+ ctx->len = size;
return size;
}
@@ -241,14 +243,15 @@ tmpl_root_entry (tmpl_ctx_t *ctx, const char *rel, const char *link)
return 0;
}
ctx->buf = ctx->raw_buf;
+ ctx->len = size;
if (size && (ctx->index == 0)) {
ctx->buf += fmt->z_offset;
- size -= fmt->z_offset;
+ ctx->len -= fmt->z_offset;
}
++(ctx->index);
- return size;
+ return ctx->len;
}
size_t
@@ -299,8 +302,10 @@ tmpl_prov_footer (tmpl_ctx_t *ctx)
size_t
tmpl_list_header (tmpl_ctx_t *ctx)
{
- ctx->buf = ctx->format->list_header;
- return strlen(ctx->buf);
+ strcpy(ctx->raw_buf, ctx->format->list_header);
+ ctx->buf = ctx->raw_buf;
+ ctx->len = strlen(ctx->buf);
+ return ctx->len;
}
size_t
@@ -308,20 +313,26 @@ tmpl_list_entry (tmpl_ctx_t *ctx, const char *bucket, const char *key)
{
int size;
const tmpl_format_t *fmt = ctx->format;
-
- size = snprintf(ctx->raw_buf,TMPL_BUF_SIZE,fmt->list_entry,bucket,key);
- if (size >= TMPL_BUF_SIZE || size < 0) {
+ char *ptr;
+ const char *fmtstr;
+ int max;
+
+ /* tmpl_list_entry is the only thing maybe called on non-empty buf */
+ ptr = ctx->raw_buf + ctx->len;
+ max = TMPL_BUF_SIZE - ctx->len;
+ if (max <= 0) {
return 0;
}
- ctx->buf = ctx->raw_buf;
-
- if (size && (ctx->index == 0)) {
- ctx->buf += fmt->z_offset;
- size -= fmt->z_offset;
+ fmtstr = fmt->list_entry + (ctx->index == 0)*fmt->z_offset;
+ size = snprintf(ptr,max,fmtstr,bucket,key);
+ if (size >= max || size < 0) {
+ return 0;
}
+ ctx->buf = ctx->raw_buf;
+ ctx->len += size;
++(ctx->index);
- return size;
+ return ctx->len;
}
size_t
@@ -344,9 +355,10 @@ tmpl_obj_header (tmpl_ctx_t *ctx, const char *bucket, const char *key)
return 0;
}
ctx->buf = ctx->raw_buf;
+ ctx->len = size;
++(ctx->index);
- return size;
+ return ctx->len;
}
size_t
@@ -362,14 +374,15 @@ tmpl_obj_entry (tmpl_ctx_t *ctx, const char *bucket, const char *key,
return 0;
}
ctx->buf = ctx->raw_buf;
+ ctx->len = size;
if (size && (ctx->index == 0)) {
ctx->buf += fmt->z_offset;
- size -= fmt->z_offset;
+ ctx->len -= fmt->z_offset;
}
++(ctx->index);
- return size;
+ return ctx->len;
}
size_t
diff --git a/template.h b/template.h
index b45eb95..4398a60 100644
--- a/template.h
+++ b/template.h
@@ -39,7 +39,8 @@ typedef struct {
const char *base;
unsigned int index;
char raw_buf[TMPL_BUF_SIZE];
- const char *buf;
+ const char *buf;
+ int len;
} tmpl_ctx_t;
#define TMPL_CTX_DONE ((tmpl_ctx_t *)(-1))
commit 88ee0f0dfc5607fed45d7a8ca294503335044696
Author: Jim Meyering <meyering(a)redhat.com>
Date: Fri Mar 11 19:05:12 2011 +0100
maint: avoid new syntax-check failure
* iwhd.init.in: Use "printf %s" in place of "echo -n".
Technically, echo -n is fine, since we know we're using bash,
but it's easier/cleaner to make this change than to exempt
this single file from the failing "make syntax-check".
diff --git a/iwhd.init.in b/iwhd.init.in
index bf57662..59d4032 100644
--- a/iwhd.init.in
+++ b/iwhd.init.in
@@ -68,15 +68,15 @@ start() {
# This is a bit kludgey. We'll use the standard daemon
# framework once iwhd knows how to daemonize itself.
test -f $PIDFILE && kill -0 $(cat $PIDFILE) 2>/dev/null \
- && { echo -n $"$PROCESS appears to already be running"
+ && { printf %s $"$PROCESS appears to already be running"
echo_failure; echo; return 1; }
mkdir -p @localstatedir@/cache/iwhd
rm -rf @localstatedir@/cache/iwhd/*
- echo -n $"waiting for mongod to listen on $MONGOD_SERVER_SPEC"
+ printf %s $"waiting for mongod to listen on $MONGOD_SERVER_SPEC"
wait_for_mongod $MONGOD_N_SECONDS && echo_success || echo_failure
echo
- echo -n $"Starting $SERVICE daemon: "
+ printf %s $"Starting $SERVICE daemon: "
$PROCESS -c "$CONFIG_JS" $IWHD_ARGS > /var/log/iwhd.log 2>&1 &
pid=$!
RETVAL=$?
@@ -106,7 +106,7 @@ restart() {
}
reload() {
- echo -n $"Reloading $SERVICE configuration: "
+ printf %s $"Reloading $SERVICE configuration: "
killproc -p $PIDFILE $PROCESS -HUP
RETVAL=$?
13 years, 1 month
3 commits - doc/warehouse_api_final.md notes.txt rest.c t/basic template.c
by Jim Meyering
doc/warehouse_api_final.md | 6 -
notes.txt | 192 ---------------------------------------------
rest.c | 52 +++++++++++-
t/basic | 32 +++++--
template.c | 4
5 files changed, 79 insertions(+), 207 deletions(-)
New commits:
commit b57b085b8f106c8f5f187213809b44db7cf92de1
Author: Jim Meyering <meyering(a)redhat.com>
Date: Thu Mar 10 20:11:13 2011 +0100
maint: remove notes.txt
* notes.txt: Remove file. It contains some outdated (invalid)
examples. The authoritative documentation is in
doc/warehouse_api_final.md
diff --git a/notes.txt b/notes.txt
deleted file mode 100644
index 92e4646..0000000
--- a/notes.txt
+++ /dev/null
@@ -1,192 +0,0 @@
-Commands:
-
- GET /
- list top-level objects
-
- POST /_new/bucket
- create bucket with attributes
-
- GET /_providers
- list providers
-
- POST /_providers/_new/provider
- create new provider with info
-
- GET /_providers/provider
- show provider information (requires token)
-
- POST /_providers/provider
- set provider information (requires/token)
-
- GET /bucket
- list bucket contents
-
- GET /bucket?query=xxx
- query bucket contents
-
- DELETE /bucket
- delete bucket (must be empty)
-
- POST /bucket/_new/object
- create object with attributes
-
- GET /bucket/object
- get body/attr* list
-
- GET /bucket/object/body
- get object body
-
- PUT /bucket/object/body
- put object body
-
- POST /bucket/object op=push
- trigger re-replication
-
- POST /bucket/object op=pull depot=xxx
- trigger reverse replication
-
- POST /bucket/object op-check depot=xxx
- check availability
-
- DELETE /bucket/object
- delete object
-
- GET /bucket/object/attrs
- get all attributes
-
- POST /bucket/object/attrs
- set multiple attributes
-
- GET /bucket/object/attr_X
- get attribute X
-
- PUT /bucket/object/attr_X
- set attribute X
-
- DELETE /bucket/object/attr_X
- delete attribute X
-
-Formats:
-
- JSON top-level list
- [
- {
- "type": "bucket_factory",
- "path": ".../_new"
- },
- {
- "type": "provider_list",
- "path": ".../_providers"
- },
- {
- "type": "bucket",
- "name": "bucketA",
- "path": ".../bucketA"
- },
- {
- "type": "bucket",
- "name": "bucketB",
- "path": ".../bucketB"
- }
- ]
-
- JSON provider list
- [
- {
- "name": "my primary",
- "type": "cf",
- "host": "swift.usersys.redhat.com",
- "port": 8080,
- "username": "my_cf_username",
- "password": "my_cf_password"
- },
- {
- "name": "my secondary",
- "type": "s3",
- "host": "s3.amazonaws.com",
- "port": 80,
- "username": "my_aws_key",
- "password": "my_aws_secret"
- }
- }
-
- JSON bucket-level list
- [
- {
- "type": "object_factory",
- "path": ".../bucket/_new"
- },
- {
- "type": "object",
- "name": "objectC",
- "path": ".../bucketA/objectC"
- },
- {
- "type": "object",
- "name": "objectD",
- "path": ".../bucketA/objectD"
- }
- ]
-
- JSON object-level list
- [
- {
- "type": "body",
- "path": ".../bucketA/objectC/body"
- },
- {
- "type": "multi_attributes",
- "path": ".../bucketA/objectC/attrs"
- },
- {
- "type": "single_attribute",
- "name": "abc",
- "path": ".../bucketA/objectC/attr_abc"
- }
- {
- "type": "single_attribute",
- "name": "xyz",
- "path": ".../bucketA/objectC/attr_xyz"
- }
- ]
-
-
-To Do - priority (1 highest) work (5 largest) desc:
- 1 1 policy inheritance
- 1 2 delete metadata as well as data
- 1 3 re-replicate on policy change (single object)
- 2 2 manual re-replication trigger
- 2 1 re-replicate on *any* tag change
- 2 3 replication-complete API
- 2 5 basic cred-management API
- 2 3 content types
- --- done
- 2 1 start own MongoDB
- 2 2 reverse-replication API
- 2 4 link-following syntax
- 2 5 reconcile dispatch with commands/format above
- 3 5 fully modular FS/S3/CF driver structure
- 3 5 VMWare back end
- 3 5 EBS back end
- 3 5 RHEV back end (dependency on Ayal)
- * Mark McLoughlin / Eoghan Glynn / rhevm-api
- 3 5 start own Hail
- 3 5 fully dynamic config
- 3 3 fix string handling (eliminate strtok)
- 3 4 re-replicate on policy change (default)
- 3 4 writes proxied upstream
- 4 1 direct-copy API (PUT with special header?)
- 4 2 use MongoDB C driver now that it's supported
- 4 3 chunked encoding
- 4 3 statistics/usage API
- 4 3 enhanced query syntax (limits)
- 4 4 optimize re-replication (check current locs)
- 4 5 dcloud-obj back end
- 4 5 immediate inline replication
- 4 5 generate MongoDB queries from ours
- 4 5 auth
- 5 2 deletes proxied upstream
- 5 3 cache control (TTL?)
- 5 5 replicated DB
- partial-object reads
- partial-object writes?
commit b6672826972980fb440a0fd6ea158b6845ede12f
Author: Jim Meyering <meyering(a)redhat.com>
Date: Thu Mar 10 19:51:45 2011 +0100
adjust code, doc and tests to use ".../_attrs", not ".../attrs"
The latter never worked. Now, GET with the former does.
* doc/warehouse_api_final.md: s,/attrs,/_attrs,
* t/basic: Likewise.
* template.c (xml_obj_header, json_obj_header): Likewise.
diff --git a/doc/warehouse_api_final.md b/doc/warehouse_api_final.md
index dd7ea57..31adfe9 100644
--- a/doc/warehouse_api_final.md
+++ b/doc/warehouse_api_final.md
@@ -140,7 +140,7 @@ elements as shown here:
$ curl http://fserver-1:9090/my_bucket/file1
<object>
<object_body path="http://fserver-1:9090/my_bucket/file1/body"/>
- <object_attr_list path="http://fserver-1:9090/my_bucket/file1/attrs"/>
+ <object_attr_list path="http://fserver-1:9090/my_bucket/file1/_attrs"/>
<object_attr name="xyz" path="http://fserver-1:9090/my_bucket/file1/attr_xyz"/>
</object>
@@ -149,7 +149,7 @@ and can have any HTTP/MIME type. The attribute-list element
can be used to fetch or set multiple attributes - including values
- at once. To fetch:
- $ curl http://fserver-1:9090/my_bucket/file1/attrs
+ $ curl http://fserver-1:9090/my_bucket/file1/_attrs
<attributes>
<attribute name="color">blue</attribute>
<attribute name="flavor">lemon</attribute>
@@ -158,7 +158,7 @@ can be used to fetch or set multiple attributes - including values
To set both of these attributes at once:
$ curl -d color="blue" -d flavor="lemon" \
- http://fserver-1:9090/my_bucket/file1/attrs
+ http://fserver-1:9090/my_bucket/file1/_attrs
Single-attribute operations are also supported. To fetch a
single attribute:
diff --git a/t/basic b/t/basic
index b902adf..e524909 100644
--- a/t/basic
+++ b/t/basic
@@ -175,7 +175,7 @@ test "$(curl $bucket/attr_post/size)" = big || fail=1
cat <<EOF > exp-attr
<object>
<object_body path="$bucket/attr_post/body"/>
- <object_attr_list path="$bucket/attr_post/attrs"/>
+ <object_attr_list path="$bucket/attr_post/_attrs"/>
<object_attr name="shape" path="$bucket/attr_post/attr_shape"/>
<object_attr name="size" path="$bucket/attr_post/attr_size"/>
</object>
diff --git a/template.c b/template.c
index 019dac6..4e2f315 100644
--- a/template.c
+++ b/template.c
@@ -75,7 +75,7 @@ static const char xml_list_footer[] = "\
static const char xml_obj_header[] = "\
<object>\n\
<object_body path=\"http://%s/%s/%s/body\"/>\n\
- <object_attr_list path=\"http://%s/%s/%s/attrs\"/>\
+ <object_attr_list path=\"http://%s/%s/%s/_attrs\"/>\
";
static const char xml_obj_entry[] = "\
@@ -166,7 +166,7 @@ static const char json_list_footer[] = "\
static char json_obj_header[] = "\
{\n\
\"object_body\": \"http://%s/%s/%s/body\"\n\
- \"object_attr_list\": \"http://%s/%s/%s/attrs\"\
+ \"object_attr_list\": \"http://%s/%s/%s/_attrs\"\
";
static char json_obj_entry[] = "\
commit 1d05af0593621f31e8cfce1472a6e81d58d47009
Author: Jim Meyering <meyering(a)redhat.com>
Date: Thu Mar 10 17:08:24 2011 +0100
allow "GET http://host/bucket/object/_attrs" to list attributes
* rest.c (URL_LIST_ATTRS): New enum member.
(reserved_attr): Add "_attrs".
(show_parts): Add forward declaration.
(proxy_list_attrs): New function.
(show_parts): Move a decl down.
(my_rules): Add a new entry to handle GET + URL_LIST_ATTRS.
(parse_url): Parse a "BUCKET/OBJECT/_attrs" URL as URL_LIST_ATTRS.
* t/basic: Automatically compare hard-coded list of reserved
attribute names and the list in rest.c, rather than relying
on a FIXME comment to help us keep things in sync.
Test new GET http://host/bucket/object/_attrs as well as
legacy (now declared to be "deprecated") op=parts interface.
diff --git a/rest.c b/rest.c
index 4681871..84757e9 100644
--- a/rest.c
+++ b/rest.c
@@ -75,7 +75,8 @@ enum { CB_BLOCK_SIZE = 64 * 1024 };
typedef enum {
URL_ROOT=0, URL_BUCKET, URL_OBJECT, URL_ATTR, URL_INVAL,
- URL_QUERY, URL_PROVLIST, URL_PROVIDER, URL_PROVIDER_SET_PRIMARY
+ URL_QUERY, URL_PROVLIST, URL_PROVIDER, URL_PROVIDER_SET_PRIMARY,
+ URL_LIST_ATTRS
} url_type;
typedef struct {
@@ -88,7 +89,7 @@ static unsigned short my_port = MY_PORT;
char *cfg_file = NULL;
static const char *const (reserved_name[]) = {"_default", "_new", "_policy", "_query", NULL};
-static const char *const (reserved_attr[]) = {"_bucket", "_date", "_etag", "_key", "_loc", "_size", NULL};
+static const char *const (reserved_attr[]) = {"_attrs", "_bucket", "_date", "_etag", "_key", "_loc", "_size", NULL};
static const char *const (reserved_bucket_name[]) = {"_new", "_providers", NULL};
static int
@@ -521,6 +522,47 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
return MHD_YES;
}
+static int show_parts (struct MHD_Connection *conn, my_state *ms);
+
+static int
+proxy_list_attrs (void *cctx, struct MHD_Connection *conn, const char *url,
+ const char *method, const char *version, const char *data,
+ size_t *data_size, void **rctx)
+{
+ (void)cctx;
+ (void)method;
+ (void)version;
+
+ my_state *ms = *rctx;
+ if (ms->state == MS_NEW) {
+ ms->state = MS_NORMAL;
+ }
+ else if (*data_size) {
+ MHD_post_process(ms->post,data,*data_size);
+ *data_size = 0;
+ }
+ else {
+ int rc = show_parts(conn,ms);
+ if (rc != MHD_HTTP_PROCESSING) {
+ /*
+ * MHD_HTTP_PROCESSING is a special response that
+ * means a request-specific routine (e.g. show_parts)
+ * created its own response. Therefore we shouldn't.
+ */
+ struct MHD_Response *resp
+ = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
+ if (!resp) {
+ fprintf(stderr,"MHD_crfd failed\n");
+ return MHD_NO;
+ }
+ MHD_queue_response(conn,rc,resp);
+ MHD_destroy_response(resp);
+ }
+ }
+
+ return MHD_YES;
+}
+
static int
proxy_get_attr (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
@@ -1400,13 +1442,13 @@ parts_callback (void *ctx, uint64_t pos, char *buf, size_t max)
static int
show_parts (struct MHD_Connection *conn, my_state *ms)
{
- struct MHD_Response *resp;
ms->aquery = meta_get_attrs(ms->bucket,ms->key);
if (!ms->aquery) {
return MHD_HTTP_NOT_FOUND;
}
+ struct MHD_Response *resp;
resp = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
CB_BLOCK_SIZE, parts_callback, ms, simple_closer);
if (!resp) {
@@ -1981,6 +2023,8 @@ static const rule my_rules[] = {
"GET", URL_OBJECT, proxy_get_data },
{ /* get attribute data */
"GET", URL_ATTR, proxy_get_attr },
+ { /* list an object's attributes */
+ "GET", URL_LIST_ATTRS, proxy_list_attrs },
{ /* put object data */
"PUT", URL_OBJECT, proxy_put_data },
{ /* put attribute data */
@@ -2065,6 +2109,8 @@ parse_url (const char *url, my_state *ms)
&& !strcmp (parts[URL_BUCKET], "_providers")
&& !strcmp (parts[URL_ATTR], "_primary"))
eindex = URL_PROVIDER_SET_PRIMARY;
+ else if (eindex == URL_ATTR && !strcmp (parts[URL_ATTR], "_attrs"))
+ eindex = URL_LIST_ATTRS;
DPRINTF("parse_url: %d: %s %s %s", eindex, parts[URL_BUCKET],
parts[URL_OBJECT], parts[URL_ATTR]);
diff --git a/t/basic b/t/basic
index 82005e2..b902adf 100644
--- a/t/basic
+++ b/t/basic
@@ -172,8 +172,6 @@ curl -d _key=attr_post -d shape=round -d size=big $bucket || fail=1
test "$(curl $bucket/attr_post/shape)" = round || fail=1
test "$(curl $bucket/attr_post/size)" = big || fail=1
-# Print all attributes of an object:
-curl -d op=parts $bucket/attr_post > out-attr || fail=1
cat <<EOF > exp-attr
<object>
<object_body path="$bucket/attr_post/body"/>
@@ -182,7 +180,12 @@ cat <<EOF > exp-attr
<object_attr name="size" path="$bucket/attr_post/attr_size"/>
</object>
EOF
+# Print all attributes of an object:
+curl $bucket/attr_post/_attrs > out-attr || fail=1
compare out-attr exp-attr || fail=1
+# Do the same thing using the old, deprecated mechanism:
+curl -d op=parts $bucket/attr_post > out-attr-deprecated || fail=1
+compare out-attr-deprecated exp-attr || fail=1
# Ensure that an attempt to add reserved object name fails.
# FIXME: keep this list in sync with the one in rest.c:
@@ -197,12 +200,27 @@ for obj_name in _default _new _policy _query; do
grep " $exp\$" bad_oname.err || fail=1
done
+# Keep this list of reserved words in sync with the one in rest.c:
+attr_words='_attrs _bucket _date _etag _key _loc _size'
+
+actual=$(grep '^static.*reserved_attr' "$abs_top_srcdir/rest.c" \
+ |perl -pe 's/.*?"//;s/", NULL.*//;s/", "/ /g')
+case $attr_words in
+ "$actual") ;;
+ *) fail_ reserved attribute mismatch;;
+esac
+
# Ensure that an attempt to add each reserved attribute fails.
-# FIXME: keep this list in sync with the one in rest.c:
-# grep '^static.*reserved_attr' rest.c
-for attr in _bucket _key _date _etag _loc _size; do
+for attr in $attr_words; do
+ case $attr in
+ # _attrs is defined only for GET, so evokes 404 rather than 400
+ _attrs) exp=404;;
+ *) exp=400;;
+ esac
+
printf whatever | curl -f -T - $bucket/rsvd_attr/$attr 2> bad_attr.err
- grep ' 400$' bad_attr.err || fail=1
+ grep " $exp$" bad_attr.err || { echo got this:; cat bad_attr.err;
+ fail_ "expected $exp"; }
done
# Ensure that an attempt to create a bucket with a reserved name fails.
13 years, 1 month
.gitignore Makefile.am iwhd.init.in iwhd.spec.in
by Jim Meyering
.gitignore | 2
Makefile.am | 50 +++++++++++++++++++--
iwhd.init.in | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iwhd.spec.in | 3 +
4 files changed, 187 insertions(+), 5 deletions(-)
New commits:
commit 5aee001ff8cbd8cf0db57faee98de44dea33d4d4
Author: Jim Meyering <meyering(a)redhat.com>
Date: Tue Mar 1 19:07:09 2011 +0100
build: add init.d-style script and conf.js and install them
This init script waits for up to 2 seconds for mongod to become usable
on the specified host:port. Unlike a previous "stop" function, this one
no longer kills all iwhd processes, but only the one started by us.
* iwhd.init.in: New file, template for iwhd.init.
* iwhd.spec.in (%files): Reflect additions:
%{_sysconfdir}/rc.d/init.d/iwhd
%{_sysconfdir}/iwhd/conf.js
%{_localstatedir}/lib/iwhd
* Makefile.am (EXTRA_DIST): Distribute it.
(iwhd.init): New rule, to generate it.
(iwhd.spec): Add AM_V_GEN and AM_V_at uses, to be consistent.
(DISTCLEANFILES): Add iwhd.init and iwhd-conf.js.
(iwhd-conf.js): New rule.
(install-data-local, uninstall-local): New rules.
* .gitignore: Ignore the new generated files.
diff --git a/.gitignore b/.gitignore
index 888efbc..f5ef3e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,8 +16,10 @@
/gnulib-tests
/iwhd
/iwhd-*.tar.gz
+/iwhd-conf.js
/iwhd-qparser.c
/iwhd-qparser.h
+/iwhd.init
/iwhd.spec
/m4/.gitignore
/maint.mk
diff --git a/Makefile.am b/Makefile.am
index 4d10bf1..8726a2e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,6 +24,7 @@ ACLOCAL_AMFLAGS = -I m4
bin_PROGRAMS = iwhd dc-rhev-image
EXTRA_DIST = \
+ iwhd.init.in \
iwhd-qparser.h \
iwhd.spec \
iwhd.spec.in
@@ -63,11 +64,11 @@ VERSION_no_hyphen = $$(echo $(VERSION)|tr - _)
MOSTLYCLEANFILES += iwhd.spec
MAINTAINERCLEANFILES += iwhd.spec
iwhd.spec: iwhd.spec.in Makefile
- rm -f $@-t $@
- v=$$(echo $(VERSION)|tr - .); \
- sed 's/[@]VERSION@/'"$$v"/ $< > $@-t
- chmod a=r $@-t
- mv $@-t $@
+ $(AM_V_GEN)rm -f $@-t $@
+ $(AM_V_at)v=$$(echo $(VERSION)|tr - .); \
+ sed 's/[@]VERSION@/'"$$v"/ $< > $@-t
+ $(AM_V_at)chmod a=r $@-t
+ $(AM_V_at)mv $@-t $@
# The following rule accommodates pre-release version number strings
# like "0.0.273-1621" that contain a "-". Repack the tarball
@@ -91,6 +92,45 @@ rpm: dist iwhd.spec
*) rpmbuild -ta $(distdir).tar.gz ;; \
esac
+# Use this directory for the local FS-based store mentioned in the etc config.
+DEFAULT_FS_DIR = $(localstatedir)/lib/iwhd
+
+DISTCLEANFILES = iwhd.init
+iwhd.init: iwhd.init.in $(top_builddir)/config.status
+ $(AM_V_GEN)sed \
+ -e s!@localstatedir\@!@localstatedir@!g \
+ -e s!@sbindir\@!@sbindir@!g \
+ -e s!@sysconfdir\@!@sysconfdir@!g \
+ < $< > $@-t && \
+ chmod a+x $@-t && \
+ mv $@-t $@
+
+DISTCLEANFILES += iwhd-conf.js
+iwhd-conf.js: $(top_builddir)/config.status
+ $(AM_V_GEN)printf '%s\n' \
+ '[' \
+ ' {' \
+ ' "name": "primary",' \
+ ' "type": "fs",' \
+ ' "path": "$(DEFAULT_FS_DIR)"' \
+ ' }' \
+ ']' \
+ > $@-t && chmod a-w $@-t && mv $@-t $@
+
+.PHONY: install-data-local
+install-data-local: iwhd.init iwhd-conf.js
+ mkdir -p $(DESTDIR)$(sysconfdir)/rc.d/init.d
+ $(INSTALL_SCRIPT) iwhd.init $(DESTDIR)$(sysconfdir)/rc.d/init.d/iwhd
+ mkdir -p $(DESTDIR)$(sysconfdir)/iwhd
+ $(INSTALL_DATA) iwhd-conf.js $(DESTDIR)$(sysconfdir)/iwhd/conf.js
+ mkdir -p $(DESTDIR)$(DEFAULT_FS_DIR)
+
+.PHONY: uninstall-local
+uninstall-local:
+ rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/iwhd \
+ $(DESTDIR)$(sysconfdir)/iwhd/conf.js
+ rmdir $(DESTDIR)$(DEFAULT_FS_DIR)
+
iwhd_CPPFLAGS = $(HAIL_CFLAGS) -I$(top_srcdir)/lib
iwhd_LDADD = \
lib/libiwhd.a \
diff --git a/iwhd.init.in b/iwhd.init.in
new file mode 100644
index 0000000..bf57662
--- /dev/null
+++ b/iwhd.init.in
@@ -0,0 +1,137 @@
+#!/bin/sh
+
+# The following is the LSB init header. See
+# http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-g...
+#
+### BEGIN INIT INFO
+# Provides: iwhd
+# Default-Start: 3 4 5
+# Short-Description: image warehouse daemon
+# Description: This is the primary server process for the image warehouse
+### END INIT INFO
+
+# the following is chkconfig init header
+#
+# iwhd: image warehouse daemon
+#
+# chkconfig: - 40 60
+# Description: This is the primary server process for the image warehouse
+#
+# processname: iwhd
+# pidfile: @localstatedir(a)/run/iwhd.pid
+
+. @sysconfdir(a)/rc.d/init.d/functions
+
+SERVICE=iwhd
+PROCESS=iwhd
+PIDFILE=@localstatedir(a)/run/$SERVICE.pid
+CONFIG_JS=@sysconfdir(a)/iwhd/conf.js
+MONGOD_SERVER_SPEC=localhost:27017
+
+# How many seconds to wait for mongod to become usable before giving up.
+MONGOD_N_SECONDS=2
+
+IWHD_ARGS="-d $MONGOD_SERVER_SPEC"
+
+test -r @sysconfdir@/sysconfig/iwhd && . @sysconfdir@/sysconfig/iwhd
+
+RETVAL=0
+
+wait_for()
+{
+ local sleep_seconds=$1
+ local max_n_sleeps=$2
+ local cmd=$3
+ case $max_n_sleeps in
+ [0-9]*);; *) echo invalid max_n_sleeps $max_n_sleeps 1>&2; exit 1;;
+ esac
+ case $sleep_seconds in
+ [0-9]*|.[0-9]*);; *)
+ echo invalid sleep interval $sleep_seconds 1>&2; exit 1;;
+ esac
+ local i=0
+ while :; do
+ eval "$cmd" && return 0
+ sleep $sleep_seconds
+ i=$(expr $i + 1)
+ test $i = $max_n_sleeps && return 1
+ done
+}
+
+wait_for_mongod() {
+ # Wait for up to $1 seconds for mongod to begin listening.
+ wait_for .1 $(($1 * 10)) 'mongo $MONGOD_SERVER_SPEC \
+ < /dev/null >/dev/null 2>&1'
+}
+
+start() {
+ # This is a bit kludgey. We'll use the standard daemon
+ # framework once iwhd knows how to daemonize itself.
+ test -f $PIDFILE && kill -0 $(cat $PIDFILE) 2>/dev/null \
+ && { echo -n $"$PROCESS appears to already be running"
+ echo_failure; echo; return 1; }
+ mkdir -p @localstatedir@/cache/iwhd
+ rm -rf @localstatedir@/cache/iwhd/*
+ echo -n $"waiting for mongod to listen on $MONGOD_SERVER_SPEC"
+ wait_for_mongod $MONGOD_N_SECONDS && echo_success || echo_failure
+ echo
+
+ echo -n $"Starting $SERVICE daemon: "
+ $PROCESS -c "$CONFIG_JS" $IWHD_ARGS > /var/log/iwhd.log 2>&1 &
+ pid=$!
+ RETVAL=$?
+ if test $RETVAL = 0; then
+ echo $pid > $PIDFILE
+ touch /var/lock/subsys/$SERVICE
+ success
+ else
+ failure
+ fi
+ echo
+}
+
+stop() {
+ action $"Stopping $SERVICE daemon: " killproc -p $PIDFILE $PROCESS
+ RETVAL=$?
+ if test $RETVAL = 0; then
+ rm -f @localstatedir@/lock/subsys/$SERVICE
+ rm -f $PIDFILE
+ rm -rf @localstatedir@/cache/iwhd/*
+ fi
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ echo -n $"Reloading $SERVICE configuration: "
+
+ killproc -p $PIDFILE $PROCESS -HUP
+ RETVAL=$?
+ echo
+ return $RETVAL
+}
+
+# See how we were called.
+case "$1" in
+ start|stop|restart|reload)
+ $1
+ ;;
+ status)
+ status -p $PIDFILE $PROCESS
+ RETVAL=$?
+ ;;
+ force-reload)
+ reload
+ ;;
+ condrestart|try-restart)
+ test -f @localstatedir@/lock/subsys/$SERVICE && restart || :
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|reload|force-reload|try-restart}"
+ exit 2
+ ;;
+esac
+exit $RETVAL
diff --git a/iwhd.spec.in b/iwhd.spec.in
index 83b2859..744973a 100644
--- a/iwhd.spec.in
+++ b/iwhd.spec.in
@@ -62,6 +62,9 @@ rm -rf %{buildroot}
%{_bindir}/iwhd
%{_bindir}/dc-rhev-image
%{_mandir}/man8/iwhd.8*
+%{_sysconfdir}/rc.d/init.d/iwhd
+%{_sysconfdir}/iwhd/conf.js
+%{_localstatedir}/lib/iwhd
%changelog
* Wed Oct 7 2010 Jim Meyering <meyering(a)redhat.com> - 0.90-1
13 years, 2 months
iwhd.spec.in
by Jim Meyering
iwhd.spec.in | 1 +
1 file changed, 1 insertion(+)
New commits:
commit 6ecbfa089d005e116c825fee266004b477362af0
Author: Jim Meyering <meyering(a)redhat.com>
Date: Fri Mar 4 15:23:59 2011 +0100
fix "make rpm" to reflect a recent addition
* iwhd.spec.in (%files): Add %{_bindir}/dc-rhev-image.
diff --git a/iwhd.spec.in b/iwhd.spec.in
index b6ec514..83b2859 100644
--- a/iwhd.spec.in
+++ b/iwhd.spec.in
@@ -60,6 +60,7 @@ rm -rf %{buildroot}
%defattr(-,root,root,-)
%doc AUTHORS COPYING README NEWS
%{_bindir}/iwhd
+%{_bindir}/dc-rhev-image
%{_mandir}/man8/iwhd.8*
%changelog
13 years, 2 months
gl/lib
by Jim Meyering
gl/lib/copy-file.c.diff | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
New commits:
commit b0019552e333950ee6bcd971fddb672cf6670bf1
Author: Jim Meyering <meyering(a)redhat.com>
Date: Fri Mar 4 08:03:27 2011 +0100
build: work around -Wunused-result warning in copy-file.c
Without this change, ./configure --enable-gcc-warnings && make
would fail due to the warning-inducing code pulled from gnulib.
* gl/lib/copy-file.c.diff: New file, to make bootstrap (via
gnulib-tool) automatically patch gnulib's copy-file.c,
while we wait for it to be fixed in gnulib proper.
diff --git a/gl/lib/copy-file.c.diff b/gl/lib/copy-file.c.diff
new file mode 100644
index 0000000..5fe69b5
--- /dev/null
+++ b/gl/lib/copy-file.c.diff
@@ -0,0 +1,33 @@
+From 5d96323f5cf9b4b4b52ae0b40d5c6ce7a4dfcd29 Mon Sep 17 00:00:00 2001
+From: Jim Meyering <meyering(a)redhat.com>
+Date: Tue, 1 Mar 2011 15:20:33 +0100
+Subject: [PATCH] copy-file: ignore warning about chown's unused return value
+
+* lib/copy-file.c: Include "ignore-value.h".
+(copy_file_preserving): Ignore chown failure.
+---
+ ChangeLog | 7 +++++++
+ lib/copy-file.c | 3 ++-
+ 3 files changed, 10 insertions(+), 1 deletions(-)
+
+diff --git a/lib/copy-file.c b/lib/copy-file.c
+index f9cd9c0..27a35c2 100644
+--- a/lib/copy-file.c
++++ b/lib/copy-file.c
+@@ -42,6 +42,7 @@
+ #include "acl.h"
+ #include "binary-io.h"
+ #include "gettext.h"
++#include "ignore-value.h"
+ #include "xalloc.h"
+
+ #define _(str) gettext (str)
+@@ -117,7 +118,7 @@ copy_file_preserving (const char *src_filename, const char *dest_filename)
+
+ #if HAVE_CHOWN
+ /* Preserve the owner and group. */
+- chown (dest_filename, statbuf.st_uid, statbuf.st_gid);
++ ignore_value (chown (dest_filename, statbuf.st_uid, statbuf.st_gid));
+ #endif
+
+ /* Preserve the access permissions. */
13 years, 2 months
4 commits - backend.c bootstrap.conf configure.ac dc-rhev-image.c doc/warehouse_api_final.md .gitignore gnulib-tests/.gitignore iwhd.spec.in lib/.gitignore Makefile.am rest.c setup.c
by Pete Zaitcev
.gitignore | 1
Makefile.am | 13
backend.c | 291 ++++++++
bootstrap.conf | 3
configure.ac | 5
dc-rhev-image.c | 1566 +++++++++++++++++++++++++++++++++++++++++++++
doc/warehouse_api_final.md | 10
gnulib-tests/.gitignore | 22
iwhd.spec.in | 1
lib/.gitignore | 22
rest.c | 4
setup.c | 10
12 files changed, 1926 insertions(+), 22 deletions(-)
New commits:
commit de21d4c71346135af16a7f9d23593f93773cb396
Author: Pete Zaitcev <zaitcev(a)yahoo.com>
Date: Thu Mar 3 23:08:48 2011 -0700
Cleanups:
exit(EXIT_FAILURE)
fprintf -> error
unused valiables
stub with ignore_value()
diff --git a/backend.c b/backend.c
index 89340d1..5b2fd40 100644
--- a/backend.c
+++ b/backend.c
@@ -42,6 +42,7 @@
#include "mpipe.h"
#include "backend.h"
#include "state_defs.h"
+#include "ignore-value.h"
#define NFSGID 36
@@ -1332,7 +1333,7 @@ fs_bcreate (const provider_t *prov, const char *bucket)
* dc-rhev-image cannot do setregid either, so no harm done:
* probably this is not a RHEV back-end to begin with.
*/
- chown(bucket,-1,NFSGID);
+ ignore_value(chown(bucket,-1,NFSGID));
return MHD_HTTP_OK;
}
diff --git a/bootstrap.conf b/bootstrap.conf
index 2e7cb31..c64c7ac 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -36,6 +36,7 @@ gnu-web-doc-update
gnupload
hash
hash-pjw
+ignore-value
malloc-gnu
maintainer-makefile
manywarnings
diff --git a/dc-rhev-image.c b/dc-rhev-image.c
index ca991eb..aff22fa 100644
--- a/dc-rhev-image.c
+++ b/dc-rhev-image.c
@@ -22,8 +22,9 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-/* need _GNU_SOURCE for asprintf */
-#define _GNU_SOURCE
+
+#include <config.h>
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -105,7 +106,7 @@ struct api_conn {
static void Usage(void)
{
fprintf(stderr, "ERROR Usage: " TAG " [" TAG ".conf]\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
static char **env_p;
@@ -229,7 +230,8 @@ static int path_exists(const char *path)
}
static void
-cfg_veripick(char **cfgval, const char *cfgname, json_t *jcfg, char *cfgtag)
+cfg_veripick(char **cfgval, const char *cfgname, json_t *jcfg,
+ const char *cfgtag)
{
json_t *elem;
const char *name;
@@ -240,18 +242,18 @@ cfg_veripick(char **cfgval, const char *cfgname, json_t *jcfg, char *cfgtag)
fprintf(stderr,
"ERROR configuration %s: tag %s is not a string\n",
cfgname, cfgtag);
- exit(2);
+ exit(EXIT_FAILURE);
}
name = json_string_value(elem);
if (!name) {
fprintf(stderr, "ERROR configuration %s: tag %s has no value\n",
cfgname, cfgtag);
- exit(2);
+ exit(EXIT_FAILURE);
}
tmp = strdup(name);
if (!tmp) {
fprintf(stderr, "ERROR no core\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
*cfgval = tmp;
}
@@ -311,19 +313,19 @@ static void apipaths(struct api_conn *connection, struct curl_slist *headers,
rcc = curl_easy_perform(curl);
if (rcc != CURLE_OK) {
fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
doc = xmlReadMemory(apib.buf, apib.used, "rhev-api.xml", NULL, 0);
if (doc == NULL) {
fprintf(stderr, "ERROR API parse error in `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
etroot = xmlDocGetRootElement(doc);
if (strcmp((const char *)etroot->name, "api") != 0) {
fprintf(stderr, "ERROR API root is `api'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
for (et = etroot->children; et; et = et->next) {
@@ -358,11 +360,11 @@ static void apipaths(struct api_conn *connection, struct curl_slist *headers,
if (!pathsd) {
fprintf(stderr, "ERROR API has no `rel=storagedomains'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
if (!pathdc) {
fprintf(stderr, "ERROR API has no `rel=datacenters'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
*psd = pathsd;
@@ -376,11 +378,11 @@ static void apipaths(struct api_conn *connection, struct curl_slist *headers,
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
+ exit(EXIT_FAILURE);
err_href:
fprintf(stderr, "ERROR <link /> with no `href'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
static xmlNode *xmlGetChild(xmlNode *node, const char *name)
@@ -450,13 +452,13 @@ static struct stor_dom *apistordom_1(struct config *cfg, xmlChar *uuidsd,
if (!(sd = malloc(sizeof(struct stor_dom)))) {
fprintf(stderr, "ERROR No core\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
memset(sd, 0, sizeof(struct stor_dom));
sd->uuid = strdup((char *)uuidsd);
if (!sd->uuid) {
fprintf(stderr, "ERROR No core\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
sd->address = cfg->nfshost;
sd->path = cfg->nfspath;
@@ -503,19 +505,19 @@ static struct stor_dom *apistordom(struct config *cfg,
rcc = curl_easy_perform(curl);
if (rcc != CURLE_OK) {
fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
doc = xmlReadMemory(apib.buf, apib.used, "rhev-api.xml", NULL, 0);
if (doc == NULL) {
fprintf(stderr, "ERROR API parse error in `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
etroot = xmlDocGetRootElement(doc);
if (strcmp((const char *)etroot->name, "storage_domains") != 0) {
fprintf(stderr, "ERROR API root is `storage_domains'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
sd = NULL;
@@ -562,7 +564,7 @@ static struct stor_dom *apistordom(struct config *cfg,
if (!sd) {
fprintf(stderr, "ERROR NFS storage domain for `%s' not found\n",
url);
- exit(1);
+ exit(EXIT_FAILURE);
}
xmlFreeDoc(doc);
@@ -573,8 +575,7 @@ static struct stor_dom *apistordom(struct config *cfg,
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
- return NULL;
+ exit(EXIT_FAILURE);
}
static int apipoolid(struct config *cfg, struct api_conn *connection,
@@ -611,19 +612,19 @@ static int apipoolid(struct config *cfg, struct api_conn *connection,
rcc = curl_easy_perform(curl);
if (rcc != CURLE_OK) {
fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
doc = xmlReadMemory(apib.buf, apib.used, "rhev-api.xml", NULL, 0);
if (doc == NULL) {
fprintf(stderr, "ERROR API parse error in `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
etroot = xmlDocGetRootElement(doc);
if (strcmp((const char *)etroot->name, "storage_domains") != 0) {
fprintf(stderr, "ERROR API root is `storage_domains'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
for (etsd = etroot->children; etsd; etsd = etsd->next) {
@@ -656,8 +657,7 @@ ret:
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
- return 0;
+ exit(EXIT_FAILURE);
}
static char *apipool(struct config *cfg, struct api_conn *connection,
@@ -697,7 +697,7 @@ static char *apipool(struct config *cfg, struct api_conn *connection,
rcc = curl_easy_perform(curl);
if (rcc != CURLE_OK) {
fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
/* <--- cleanup curl connection here if needed */
@@ -705,13 +705,13 @@ static char *apipool(struct config *cfg, struct api_conn *connection,
doc = xmlReadMemory(apib.buf, apib.used, "rhev-api-dc.xml", NULL, 0);
if (doc == NULL) {
fprintf(stderr, "ERROR API parse error in `%s'\n", url);
- exit(1);
+ exit(EXIT_FAILURE);
}
etroot = xmlDocGetRootElement(doc);
if (strcmp((const char *)etroot->name, "data_centers") != 0) {
fprintf(stderr, "ERROR API root is `data_centers'\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
for (etdc = etroot->children; etdc; etdc = etdc->next) {
@@ -768,13 +768,11 @@ static char *apipool(struct config *cfg, struct api_conn *connection,
}
fprintf(stderr, "ERROR pool not found for storage domain %s\n", sd_uuid);
- exit(1);
- return NULL;
+ exit(EXIT_FAILURE);
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
- return NULL;
+ exit(EXIT_FAILURE);
}
static struct stor_dom *apistart(struct config *cfg)
@@ -797,7 +795,7 @@ static struct stor_dom *apistart(struct config *cfg)
if (!huri_parse(&huri, cfg->apiurl)) {
fprintf(stderr, "ERROR unable to parse `apiurl': `%s'\n",
cfg->apiurl);
- exit(1);
+ exit(EXIT_FAILURE);
}
path = strndup(huri.path, huri.path_len);
@@ -874,7 +872,7 @@ static struct stor_dom *apistart(struct config *cfg)
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
static void spitovf(struct config *cfg, struct stor_dom *sd,
@@ -906,7 +904,7 @@ static void spitovf(struct config *cfg, struct stor_dom *sd,
if (mkdir(tmpovfdir, 0770) < 0) {
fprintf(stderr, "ERROR Failed to make directory %s: %s\n",
tmpovfdir, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
uuid_unparse_lower(img_uuid, uuidbuf);
@@ -931,13 +929,13 @@ static void spitovf(struct config *cfg, struct stor_dom *sd,
if (!writer) {
fprintf(stderr, "ERROR Error creating the xml driver for %s\n",
tmpovf);
- exit(1);
+ exit(EXIT_FAILURE);
}
rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
if (rc < 0) {
fprintf(stderr, "ERROR Error in xmlTextWriterStartDocument\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
rc = xmlTextWriterStartElement(writer, BAD_CAST "ovf:Envelope");
@@ -1325,7 +1323,7 @@ static void spitovf(struct config *cfg, struct stor_dom *sd,
rc = xmlTextWriterEndDocument(writer);
if (rc < 0) {
fprintf(stderr, "ERROR Error in xmlTextWriterEndDocument\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
xmlFreeTextWriter(writer);
@@ -1333,12 +1331,12 @@ static void spitovf(struct config *cfg, struct stor_dom *sd,
if (rename(tmpimgdir, imgdir) < 0) {
fprintf(stderr, "ERROR Failed to rename from %s to %s: %s\n",
tmpimgdir, imgdir, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
if (rename(tmpovfdir, ovfdir) < 0) {
fprintf(stderr, "ERROR Failed to rename from %s to %s: %s\n",
tmpovfdir, ovfdir, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
free(domdir);
@@ -1351,11 +1349,11 @@ static void spitovf(struct config *cfg, struct stor_dom *sd,
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
+ exit(EXIT_FAILURE);
err_xml:
fprintf(stderr, "ERROR Failed to form XML\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
@@ -1378,7 +1376,7 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
if (geteuid() != NFSUID || getegid() != NFSGID) {
fprintf(stderr,
"ERROR Have to run with user and group 36\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
}
@@ -1388,11 +1386,11 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
if (stat(domdir, &statb) < 0) {
fprintf(stderr, "ERROR failed to stat %s: %s\n",
domdir, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
if (!S_ISDIR(statb.st_mode)) {
fprintf(stderr, "ERROR path %s is not a directory\n", domdir);
- exit(1);
+ exit(EXIT_FAILURE);
}
uuid_generate_random(vol_uuid);
@@ -1407,7 +1405,7 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
if (mkdir(tmpimgdir, 0770) < 0) {
fprintf(stderr, "ERROR Failed to make directory %s: %s\n",
tmpimgdir, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
now = time(NULL);
@@ -1424,7 +1422,7 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
if (stat(imgdst, &statb) < 0) {
fprintf(stderr, "ERROR failed to stat %s: %s\n",
imgdst, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
vol_size = statb.st_size;
@@ -1436,7 +1434,7 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
if (!fp) {
fprintf(stderr, "ERROR Failed to create %s: %s\n",
imgmeta, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
fprintf(fp, "DOMAIN=%s\n", sd->uuid);
/* saved template has VOLTYPE=SHARED */
@@ -1458,7 +1456,7 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
fprintf(fp, "EOF\n");
if (close_stream(fp)) {
fprintf(stderr, "ERROR Failed to write %s\n", imgmeta);
- exit(1);
+ exit(EXIT_FAILURE);
}
spitovf(cfg, sd, img_uuid, vol_uuid, vol_size, tpl_uuid);
@@ -1471,14 +1469,14 @@ static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
err_alloc:
fprintf(stderr, "ERROR No core\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
int main(int argc, char **argv, char **envp)
{
uuid_t tpl_uuid;
json_error_t err;
- char *cfgname;
+ const char *cfgname;
struct config cfg;
json_t *jcfg;
struct stor_dom *sd;
@@ -1503,21 +1501,21 @@ int main(int argc, char **argv, char **envp)
if (!jcfg) {
fprintf(stderr, "ERROR configuration JSON error %s:%d: %s\n",
cfgname, err.line, err.text);
- exit(2);
+ exit(EXIT_FAILURE);
}
if (json_typeof(jcfg) != JSON_OBJECT) {
fprintf(stderr,
"ERROR configuration JSON %s: root is not a struct\n",
cfgname);
- exit(2);
+ exit(EXIT_FAILURE);
}
/* image: local filename with disk image */
cfg_veripick(&cfg.image, cfgname, jcfg, "image");
if (!path_exists(cfg.image)) {
fprintf(stderr, "ERROR image %s does not exist\n", cfg.image);
- exit(1);
+ exit(EXIT_FAILURE);
}
/* apiurl: the so-called "base", usually "rhev-api" */
cfg_veripick(&cfg.apiurl, cfgname, jcfg, "apiurl");
@@ -1548,11 +1546,11 @@ int main(int argc, char **argv, char **envp)
cfg_veripick(&cfg.nfsdir, cfgname, jcfg, "nfsdir");
if (cfg.nfsdir[0] == 0) {
fprintf(stderr, "ERROR configuration: `nfsdir' is empty\n");
- exit(2);
+ exit(EXIT_FAILURE);
}
if (cfg.nfsdir[0] != '/') {
fprintf(stderr, "ERROR configuration: `nfsdir' is relative\n");
- exit(2);
+ exit(EXIT_FAILURE);
}
json_decref(jcfg);
diff --git a/lib/.gitignore b/lib/.gitignore
index f7907b8..61eef3f 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -55,6 +55,7 @@
/hash-pjw.h
/hash.c
/hash.h
+/ignore-value.h
/intprops.h
/inttypes.h
/inttypes.in.h
diff --git a/rest.c b/rest.c
index e89e140..4681871 100644
--- a/rest.c
+++ b/rest.c
@@ -1746,8 +1746,6 @@ proxy_primary_prov (void *cctx, struct MHD_Connection *conn, const char *url,
DPRINTF("PROXY GET PRIMARY PROVIDER (%s)\n", url);
- my_state *ms = *rctx;
-
// "/_providers/_primary" is the only one we accept for now.
bool valid = strcmp (url, "/_providers/_primary") == 0;
unsigned int rc = (valid ? MHD_HTTP_OK : MHD_HTTP_BAD_REQUEST);
@@ -1780,7 +1778,6 @@ proxy_set_primary (void *cctx, struct MHD_Connection *conn, const char *url,
DPRINTF("PROXY SET PRIMARY PROVIDER (%s)\n", url);
- my_state *ms = *rctx;
char *name = NULL;
unsigned int rc = MHD_HTTP_BAD_REQUEST;
@@ -1835,7 +1832,6 @@ proxy_delete_prov (void *cctx, struct MHD_Connection *conn, const char *url,
(void)data;
(void)data_size;
- my_state *ms = *rctx;
struct MHD_Response *resp
= MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
if (!resp) {
diff --git a/setup.c b/setup.c
index 13b9943..c1791f7 100644
--- a/setup.c
+++ b/setup.c
@@ -589,7 +589,7 @@ auto_config(void)
config = json_loads(auto_json,&err);
if (!config) {
- fprintf(stderr,"JSON error on line %d: %s\n",err.line,err.text);
+ error(0, 0, "JSON error on line %d: %s", err.line, err.text);
return NULL;
}
commit 55dbf28e2eb8eecfe90a715d3694c4457917762e
Author: Pete Zaitcev <zaitcev(a)yahoo.com>
Date: Thu Mar 3 23:03:11 2011 -0700
[patch iwhd 3/3] Touch up the warehouse_final.md
This patch fixes the syntax of curl examples and corrects the wording
about the configuration default to match reality.
Signed-off-by: Pete Zaitcev <zaitcev(a)redhat.com>
diff --git a/doc/warehouse_api_final.md b/doc/warehouse_api_final.md
index b5d7294..dd7ea57 100644
--- a/doc/warehouse_api_final.md
+++ b/doc/warehouse_api_final.md
@@ -157,7 +157,8 @@ can be used to fetch or set multiple attributes - including values
To set both of these attributes at once:
- $ curl -d color="blue" -d flavor="lemon" http://fserver-1:9090/my_bucket/file1/attrs
+ $ curl -d color="blue" -d flavor="lemon" \
+ http://fserver-1:9090/my_bucket/file1/attrs
Single-attribute operations are also supported. To fetch a
single attribute:
@@ -282,7 +283,8 @@ object body except that there's no data transfer from the client).
The second control operation is used to determine whether replication
to a specific remote warehouse has finished.
- $ curl -d op=check loc=backup http://fserver-1:9090/my_bucket/file1
+ $ curl -d op=check -d loc=backup \
+ http://fserver-1:9090/my_bucket/file1
This will return a 404 (Not Found) if the object has not been replicated
to that location, or a 200 (OK) if it has.
@@ -291,8 +293,8 @@ Appendix 2: JSON Configuration Format
-------------------------------------
The initial configuration for the image warehouse is pulled
-from a JSON configuration file, repo.json in the current directory
-by default. This defines a set of required attributes plus any
+from a JSON configuration file (set with -c option).
+This defines a set of required attributes plus any
others that the user might want to use in replication policies.
Here's an example:
commit 9fbcee7abb4ba29ab8a77e3b3022663eb7962c29
Author: Pete Zaitcev <zaitcev(a)yahoo.com>
Date: Thu Mar 3 23:02:18 2011 -0700
[patch iwhd 2/3] Fix error handling and waitpid
During the debugging of RHEV-M registration it became evident that if you
fork a child, you have to process the child's output first, and then call
waitpid(). Doing otherwise risks deadlocking if child overflows pipe.
Also, any error messages are invisible, because in case of nonzero exit
code we bail out before they are read from the pipe.
This patch adjusts s3_register to be uniform with fs_rhevm_register,
which is tested to do it right. Jeff Darcy is apprised and agrees.
Conversely, this fixes up fs_rhevm_register to use the cleanup scheme
of organ[] that s3_register has, because it is correct.
One final thing: fgets() differs from strncpy(): it always stores the
terminating nul. So, don't forcenul at [sizeof(buf)-1]. However, we
should kill the newline, or else debugging output looks ugly.
Signed-off-by: Pete Zaitcev <zaitcev(a)redhat.com>
diff --git a/backend.c b/backend.c
index 5457303..89340d1 100644
--- a/backend.c
+++ b/backend.c
@@ -375,8 +375,10 @@ s3_register (my_state *ms, const provider_t *prov, const char *next,
int argc = 0;
pid_t pid;
int organ[2];
+ int wstat;
FILE *fp;
char buf[LINE_SIZE];
+ char *s;
char *cval = NULL;
char *kval = NULL;
int rc = MHD_HTTP_BAD_REQUEST;
@@ -517,22 +519,16 @@ s3_register (my_state *ms, const provider_t *prov, const char *next,
error (EXIT_FAILURE, errno, "failed to run command %s", cmd);
}
- DPRINTF("waiting for child...\n");
- if (waitpid(pid,NULL,0) < 0) {
- error (0, errno, "waitpid failed");
- }
- /* TBD: check identity/status from waitpid */
- DPRINTF("...child exited\n");
-
close(organ[1]);
fp = fdopen(organ[0],"r");
if (!fp) {
- DPRINTF("could not open parent pipe\n");
+ error (0, 0, "could not open parent pipe stream");
close(organ[0]);
goto cleanup;
}
while (fgets(buf,sizeof(buf)-1,fp)) {
- buf[sizeof(buf)-1] = '\0';
+ if ((s = strchr(buf, '\n')) != NULL)
+ *s = '\0';
if (regexec(&s3_success_pat,buf,2,match,0) == 0) {
buf[match[1].rm_eo] = '\0';
DPRINTF("found AMI ID: %s\n",buf+match[1].rm_so);
@@ -551,6 +547,22 @@ s3_register (my_state *ms, const provider_t *prov, const char *next,
}
fclose(fp);
+ DPRINTF("waiting for child...\n");
+ if (waitpid(pid,&wstat,0) < 0) {
+ error (0, errno, "waitpid failed");
+ goto cleanup;
+ }
+ if (!WIFEXITED(wstat)) {
+ error (0, 0, "%s is killed (status 0x%x)", cmd, wstat);
+ goto cleanup;
+ }
+ if (WEXITSTATUS(wstat)) {
+ error (0, 0, "%s exited with code %d",
+ cmd, WEXITSTATUS(wstat));
+ goto cleanup;
+ }
+ DPRINTF("...child exited\n");
+
cleanup:
/*
* This is a bit tricky. If we found the cert in the HTTP request and
@@ -1350,9 +1362,6 @@ fs_rhevm_register (my_state *ms, const provider_t *prov, const char *next,
char ami_id_buf[64];
regmatch_t match[2];
- organ[0] = -1;
- organ[1] = -1;
-
if (!regex_ok) {
return MHD_HTTP_BAD_REQUEST;
}
@@ -1507,10 +1516,10 @@ fs_rhevm_register (my_state *ms, const provider_t *prov, const char *next,
}
close(organ[1]);
- organ[1] = -1;
fp = fdopen(organ[0],"r");
if (!fp) {
- error (0, 0, "could not open parent pipe stream\n");
+ error (0, 0, "could not open parent pipe stream");
+ close(organ[0]);
goto cleanup;
}
while (fgets(buf,sizeof(buf)-1,fp)) {
@@ -1532,7 +1541,6 @@ fs_rhevm_register (my_state *ms, const provider_t *prov, const char *next,
}
}
fclose(fp);
- organ[0] = -1;
DPRINTF("waiting for child...\n");
if (waitpid(pid,&wstat,0) < 0) {
@@ -1551,10 +1559,6 @@ fs_rhevm_register (my_state *ms, const provider_t *prov, const char *next,
DPRINTF("...child exited\n");
cleanup:
- if (organ[0] != -1)
- close(organ[0]);
- if (organ[1] != -1)
- close(organ[1]);
/*
* Unfortunately we cannot collect zombies indiscriminately here
* in case another thread forks. TBD: make main loop collect zombies.
commit 70d390c69a38b8446443472a1b93323c9827885f
Author: Pete Zaitcev <zaitcev(a)yahoo.com>
Date: Thu Mar 3 22:57:04 2011 -0700
Add dc-rhev-image.
diff --git a/.gitignore b/.gitignore
index f95dca3..888efbc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
/config.log
/config.status
/configure
+/dc-rhev-image
/gnulib-tests
/iwhd
/iwhd-*.tar.gz
diff --git a/Makefile.am b/Makefile.am
index d6a63b0..4d10bf1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,7 +21,7 @@ SUBDIRS = lib . gnulib-tests t man
ACLOCAL_AMFLAGS = -I m4
# iwhd is short for Image WareHouse Daemon.
-bin_PROGRAMS = iwhd
+bin_PROGRAMS = iwhd dc-rhev-image
EXTRA_DIST = \
iwhd-qparser.h \
@@ -56,6 +56,8 @@ iwhd_SOURCES = \
EXTRA_iwhd_SOURCES = qlexer.l
+dc_rhev_image_SOURCES = dc-rhev-image.c
+
VERSION_no_hyphen = $$(echo $(VERSION)|tr - _)
MOSTLYCLEANFILES += iwhd.spec
@@ -102,6 +104,15 @@ iwhd_LDADD = \
$(PTHREAD_LIB) \
$(HAIL_LIBS)
+dc_rhev_image_CPPFLAGS = $(LIBXML_CFLAGS) -I$(top_srcdir)/lib
+dc_rhev_image_LDADD = \
+ lib/libiwhd.a \
+ $(LIB_ACL) \
+ $(CURL_LIB) \
+ $(JANSSON_LIB) \
+ $(LIBXML_LIBS) \
+ $(UUID_LIB)
+
MOSTLYCLEANFILES += qlexer.c
MAINTAINERCLEANFILES += qlexer.c
EXTRA_DIST += qlexer.c
diff --git a/backend.c b/backend.c
index b44c8c0..5457303 100644
--- a/backend.c
+++ b/backend.c
@@ -43,6 +43,8 @@
#include "backend.h"
#include "state_defs.h"
+#define NFSGID 36
+
struct hstor_client *hstor;
/***** Generic module stuff, not specific to one back end *****/
@@ -54,6 +56,7 @@ struct hstor_client *hstor;
* enough to hold the header name plus a CF token (32 bytes).
*/
#define ADDR_SIZE 256
+#define LINE_SIZE 500
#define HEADER_SIZE 64
#define S3_IMAGE_PATTERN "^IMAGE[[:blank:]]+([^[:space:]]+)"
@@ -373,7 +376,7 @@ s3_register (my_state *ms, const provider_t *prov, const char *next,
pid_t pid;
int organ[2];
FILE *fp;
- char buf[ADDR_SIZE];
+ char buf[LINE_SIZE];
char *cval = NULL;
char *kval = NULL;
int rc = MHD_HTTP_BAD_REQUEST;
@@ -1308,14 +1311,264 @@ fs_bcreate (const provider_t *prov, const char *bucket)
DPRINTF("creating bucket %s\n",bucket);
- if (mkdir(bucket,0700) < 0) {
+ if (mkdir(bucket,0750) < 0) {
error (0, errno, "%s: failed to create directory", bucket);
return MHD_HTTP_INTERNAL_SERVER_ERROR;
}
+ /*
+ * The chown fails if iwhd does not run as root, but in that case
+ * dc-rhev-image cannot do setregid either, so no harm done:
+ * probably this is not a RHEV back-end to begin with.
+ */
+ chown(bucket,-1,NFSGID);
return MHD_HTTP_OK;
}
+static int
+fs_rhevm_register (my_state *ms, const provider_t *prov, const char *next,
+ Hash_table *args)
+{
+ char *api_url;
+ char *api_user;
+ char *api_secret;
+ char *nfs_host;
+ char *nfs_path;
+ char *nfs_dir;
+ const char *conf_name = NULL;
+ char *conf_text = NULL;
+ int ret = MHD_HTTP_BAD_REQUEST;
+ int rc;
+ const char *argv[12];
+ int argc = 0;
+ pid_t pid;
+ int organ[2];
+ int wstat;
+ FILE *fp;
+ char buf[LINE_SIZE];
+ char *s;
+ char ami_id_buf[64];
+ regmatch_t match[2];
+
+ organ[0] = -1;
+ organ[1] = -1;
+
+ if (!regex_ok) {
+ return MHD_HTTP_BAD_REQUEST;
+ }
+
+ DPRINTF("*** RHEV-M register %s/%s via %s (%s:%d)\n",
+ ms->bucket, ms->key, prov->name, prov->host, prov->port);
+ if (next) {
+ DPRINTF("RHEV-M register with next!=NULL\n");
+ return MHD_HTTP_BAD_REQUEST;
+ }
+
+ /*
+ * This may be a good time to check if kernel is set.
+ * Saving the code for later. Use partitioned, bootable images for now.
+ */
+#if 0
+ char *kernel = kv_hash_lookup(args,"kernel");
+ if (kernel) {
+ DPRINTF(" (using kernel %s)\n",kernel);
+ }
+#endif
+
+ /*
+ * This is always "http://xxxxxx.xxxx.xxx/rhevm-api".
+ * SSL does not work yet, sorry.
+ */
+ api_url = kv_hash_lookup(args,"api-url");
+ if (!api_url) {
+ /* TBD: This has to have a defined field in provider, too. */
+ error (0, 0, "missing RHEV-M API URL (api-url)");
+ goto cleanup;
+ }
+
+ api_user = kv_hash_lookup(args,"api-key");
+ if (!api_user) {
+ api_user = (char *)prov->username;
+ if (!api_user) {
+ error (0, 0, "missing RHEV-M API username (api-key)");
+ goto cleanup;
+ }
+ }
+
+ api_secret = kv_hash_lookup(args,"api-secret");
+ if (!api_secret) {
+ api_secret = (char *)prov->password;
+ if (!prov->password) {
+ error (0, 0, "missing RHEV-M API secret (api-secret)");
+ goto cleanup;
+ }
+ }
+
+ /*
+ * We need to document how this all works.
+ * - NFS host and NFS path are matched against the list of storage
+ * domains in RHEV-M (we fetch the list using RESTful API).
+ * This way we find the UUIDs that we have to write out.
+ * - "NFS directory" is the place where write the image and metadata.
+ * It is up to the user to make sure that nfs-dir points to
+ * nfs-host:nfs-path. We do not have a way to verify it is so.
+ */
+ nfs_host = kv_hash_lookup(args,"nfs-host");
+ nfs_path = kv_hash_lookup(args,"nfs-path");
+ nfs_dir = kv_hash_lookup(args,"nfs-dir");
+ if (!nfs_host) {
+ error (0, 0, "missing export domain's host (nfs-host)");
+ goto cleanup;
+ }
+ if (!nfs_path) {
+ error (0, 0, "missing export domain's path (nfs-path)");
+ goto cleanup;
+ }
+ if (!nfs_dir) {
+ error (0, 0, "missing target directory (nfs-dir)");
+ goto cleanup;
+ }
+
+#if 0 /* The GC library crashes on free() in this case. */
+ rc = asprintf(&conf_text,
+ "{\n"
+ " \"image\" : \"%s/%s\",\n"
+ " \"apiurl\" : \"%s\",\n"
+ " \"apiuser\" : \"%s\",\n"
+ " \"apipass\" : \"%s\",\n"
+ " \"nfshost\" : \"%s\",\n"
+ " \"nfspath\" : \"%s\",\n"
+ " \"nfsdir\" : \"%s\"\n"
+ "}\n",
+ ms->bucket, ms->key,
+ api_url, api_user, api_secret,
+ nfs_host, nfs_path, nfs_dir
+ );
+ if (rc < 0) {
+ conf_text = NULL;
+ error (0, 0, "no core");
+ goto cleanup;
+ }
+#else
+ conf_text = malloc(1000);
+ if (!conf_text) {
+ error (0, 0, "no core");
+ goto cleanup;
+ }
+ snprintf(conf_text, 1000,
+ "{\n"
+ " \"image\" : \"%s/%s\",\n"
+ " \"apiurl\" : \"%s\",\n"
+ " \"apiuser\" : \"%s\",\n"
+ " \"apipass\" : \"%s\",\n"
+ " \"nfshost\" : \"%s\",\n"
+ " \"nfspath\" : \"%s\",\n"
+ " \"nfsdir\" : \"%s\"\n"
+ "}\n",
+ ms->bucket, ms->key,
+ api_url, api_user, api_secret,
+ nfs_host, nfs_path, nfs_dir
+ );
+#endif
+ conf_name = s3_init_tmpfile(conf_text);
+ if (!conf_name) {
+ error (0, 0, "cannot create a temporary file");
+ goto cleanup;
+ }
+
+ sprintf(ami_id_buf,"pending %lld",(long long)time(NULL));
+ DPRINTF("temporary ami-id = \"%s\"\n",ami_id_buf);
+ (void)meta_set_value(ms->bucket,ms->key,"ami-id",ami_id_buf);
+ rc = MHD_HTTP_INTERNAL_SERVER_ERROR;
+
+ const char *cmd = "dc-rhev-image";
+ argv[argc++] = cmd;
+ argv[argc++] = conf_name;
+ argv[argc] = NULL;
+
+ if (pipe(organ) < 0) {
+ error (0, errno, "pipe creation failed");
+ goto cleanup;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ error (0, errno, "fork failed");
+ close(organ[0]);
+ close(organ[1]);
+ goto cleanup;
+ }
+
+ if (pid == 0) {
+ (void)dup2(organ[1],STDOUT_FILENO);
+ (void)dup2(organ[1],STDERR_FILENO);
+ execvp(cmd, (char* const*)argv);
+ error (EXIT_FAILURE, errno, "failed to run command %s", cmd);
+ }
+
+ close(organ[1]);
+ organ[1] = -1;
+ fp = fdopen(organ[0],"r");
+ if (!fp) {
+ error (0, 0, "could not open parent pipe stream\n");
+ goto cleanup;
+ }
+ while (fgets(buf,sizeof(buf)-1,fp)) {
+ if ((s = strchr(buf, '\n')) != NULL)
+ *s = '\0';
+ if (regexec(&s3_success_pat,buf,2,match,0) == 0) {
+ buf[match[1].rm_eo] = '\0';
+ DPRINTF("found image UUID: %s\n",buf+match[1].rm_so);
+ sprintf(ami_id_buf,"OK %.60s",buf+match[1].rm_so);
+ rc = MHD_HTTP_OK;
+ }
+ else if (strcmp(buf,"ERROR") == 0) {
+ DPRINTF("found err marker: %s\n",buf+sizeof("ERROR"));
+ sprintf(ami_id_buf,"failed %.56s",buf+sizeof("ERROR"));
+ rc = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ else {
+ DPRINTF("ignoring line: <%s>\n",buf);
+ }
+ }
+ fclose(fp);
+ organ[0] = -1;
+
+ DPRINTF("waiting for child...\n");
+ if (waitpid(pid,&wstat,0) < 0) {
+ error (0, errno, "waitpid failed");
+ goto cleanup;
+ }
+ if (!WIFEXITED(wstat)) {
+ error (0, 0, "%s is killed (status 0x%x)", cmd, wstat);
+ goto cleanup;
+ }
+ if (WEXITSTATUS(wstat)) {
+ error (0, 0, "%s exited with code %d",
+ cmd, WEXITSTATUS(wstat));
+ goto cleanup;
+ }
+ DPRINTF("...child exited\n");
+
+cleanup:
+ if (organ[0] != -1)
+ close(organ[0]);
+ if (organ[1] != -1)
+ close(organ[1]);
+ /*
+ * Unfortunately we cannot collect zombies indiscriminately here
+ * in case another thread forks. TBD: make main loop collect zombies.
+ */
+ // while(waitpid(-1,&wstat,WNOHANG) >= 0) {}
+ if (conf_name)
+ unlink(conf_name);
+ free((char *)conf_name);
+ free(conf_text);
+
+ (void)meta_set_value(ms->bucket,ms->key,"ami-id",ami_id_buf);
+ return ret;
+}
+
/***** Function tables. ****/
backend_func_tbl bad_func_tbl = {
@@ -1372,3 +1625,14 @@ backend_func_tbl fs_func_tbl = {
fs_bcreate,
bad_register,
};
+
+backend_func_tbl fs_rhevm_func_tbl = {
+ "FS-RHEV-M",
+ fs_init,
+ fs_get_child,
+ fs_put_child,
+ bad_cache_child,
+ fs_delete,
+ fs_bcreate,
+ fs_rhevm_register,
+};
diff --git a/bootstrap.conf b/bootstrap.conf
index 2e4cf7f..2e7cb31 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -18,10 +18,12 @@
# gnulib modules used by this package.
gnulib_modules='
announce-gen
+base64
c-ctype
calloc-posix
close
closeout
+copy-file
dirname
do-release-commit-and-tag
dup2
diff --git a/configure.ac b/configure.ac
index e124f8d..e00e6b5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,11 @@ AC_CHECK_LIB([pthread], [pthread_create],
[AC_MSG_ERROR([Missing required pthread lib])])
AC_SUBST([PTHREAD_LIB])
+AC_CHECK_LIB([uuid], [uuid_generate_random],
+ [UUID_LIB=-luuid],
+ [AC_MSG_ERROR([Missing required uuid lib])])
+AC_SUBST([UUID_LIB])
+
PKG_CHECK_MODULES([HAIL],[libhail >= 0.8])
AC_SUBST([HAIL_LIBS])
AC_SUBST([HAIL_CFLAGS])
diff --git a/dc-rhev-image.c b/dc-rhev-image.c
new file mode 100644
index 0000000..ca991eb
--- /dev/null
+++ b/dc-rhev-image.c
@@ -0,0 +1,1568 @@
+/*
+ * dc-rhev-image: register an image in RHEV
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * The URL parser is taken from GNet (by way of tabled):
+ *
+ * GNet - Networking library
+ * Copyright (C) 2000-2003 David Helder, David Bolcsfoldi, Eric Williams
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+/* need _GNU_SOURCE for asprintf */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+
+#include <curl/curl.h>
+#include <jansson.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlwriter.h>
+#include <uuid/uuid.h>
+
+#include "base64.h"
+#include "close-stream.h"
+#include "copy-file.h"
+
+/*
+ * Note that we almost never prefix with TAG due to compatibility with EC2.
+ */
+#define TAG "dc-rhev-image"
+
+#define NFSUID 36
+#define NFSGID 36
+
+static const char NULID[] = "00000000-0000-0000-0000-000000000000";
+
+struct config {
+ char *image;
+ char *apiurl;
+ char *apiuser;
+ char *apipass;
+ char *nfshost;
+ char *nfspath;
+ char *nfsdir;
+};
+
+struct http_uri {
+ const char *scheme;
+ unsigned int scheme_len;
+ const char *userinfo;
+ unsigned int userinfo_len;
+ const char *hostname;
+ unsigned int hostname_len;
+
+ unsigned int port;
+
+ const char *path;
+ unsigned int path_len;
+ const char *query;
+ unsigned int query_len;
+ const char *fragment;
+ unsigned int fragment_len; /* see FIXME in huri_parse */
+};
+
+struct stor_dom {
+ char *uuid;
+ char *address;
+ char *path;
+ char *poolid;
+};
+
+struct api_buf {
+ char *buf;
+ size_t alloc;
+ size_t used;
+};
+
+struct api_conn {
+ CURL *curl;
+ char *base;
+};
+
+static void Usage(void)
+{
+ fprintf(stderr, "ERROR Usage: " TAG " [" TAG ".conf]\n");
+ exit(1);
+}
+
+static char **env_p;
+
+/* our own ISSPACE. ANSI isspace is locale dependent */
+#define ISSPACE(C) (((C) >= 9 && (C) <= 13) || (C) == ' ')
+/* oh what the heck, use this too */
+#define ISDIGIT(C) (((C) >= '0' && (C) <= '9'))
+
+#define ASSIGN(token, ptr, len) \
+ do { \
+ uri->token = (ptr); \
+ uri->token##_len = (len); \
+ } while (0)
+
+static struct http_uri *huri_parse(struct http_uri *uri, const char *uri_text)
+{
+ const char *p, *temp;
+
+ memset(uri, 0, sizeof(*uri));
+
+ /* Skip initial whitespace */
+ p = uri_text;
+ while (*p && ISSPACE((int)*p))
+ ++p;
+ if (!*p) /* Error if it's just a string of space */
+ return NULL;
+
+ /* Scheme */
+ temp = p;
+ while (*p && *p != ':' && *p != '/' && *p != '?' && *p != '#')
+ ++p;
+ if (*p == ':') {
+ ASSIGN(scheme, temp, p - temp);
+ ++p;
+ } else /* This char is NUL, /, ?, or # */
+ p = temp;
+
+ /* Authority */
+ if (*p == '/' && p[1] == '/') {
+ p += 2;
+
+ /* Userinfo */
+ temp = p;
+ while (*p && *p != '@' && *p != '/') /* Look for @ or / */
+ ++p;
+ if (*p == '@') { /* Found userinfo */
+ ASSIGN(userinfo, temp, p - temp);
+ ++p;
+ } else
+ p = temp;
+
+ /* Hostname */
+
+ /* Check for no hostname at all (e.g. file:// URIs) */
+ if (*p == '/')
+ goto path;
+
+ /* Check for IPv6 canonical hostname in brackets */
+ if (*p == '[') {
+ p++; /* Skip [ */
+ temp = p;
+ while (*p && *p != ']')
+ ++p;
+ if ((p - temp) == 0)
+ goto error;
+ ASSIGN(hostname, temp, p - temp);
+ if (*p)
+ p++; /* Skip ] (if there) */
+ } else {
+ temp = p;
+ while (*p && *p != '/' && *p != '?' && *p != '#'
+ && *p != ':')
+ ++p;
+ if ((p - temp) == 0)
+ goto error;
+ ASSIGN(hostname, temp, p - temp);
+ }
+
+ /* Port */
+ if (*p == ':') {
+ for (++p; ISDIGIT((int)*p); ++p)
+ uri->port = uri->port * 10 + (*p - '0');
+ }
+
+ }
+
+ /* Path (we are liberal and won't check if it starts with /) */
+
+path:
+ temp = p;
+ while (*p && *p != '?' && *p != '#')
+ ++p;
+ if (p != temp)
+ ASSIGN(path, temp, p - temp);
+
+ /* Query */
+ if (*p == '?') {
+ temp = p + 1;
+ while (*p && *p != '#')
+ ++p;
+ ASSIGN(query, temp, p - temp);
+ }
+
+ /* Fragment */
+ if (*p == '#') {
+ ++p;
+ uri->fragment = p;
+ /* FIXME: assign uri->fragment_len! */
+ }
+
+ return uri;
+
+error:
+ return NULL;
+}
+
+static int path_exists(const char *path)
+{
+ return access(path, R_OK) == 0;
+}
+
+static void
+cfg_veripick(char **cfgval, const char *cfgname, json_t *jcfg, char *cfgtag)
+{
+ json_t *elem;
+ const char *name;
+ char *tmp;
+
+ elem = json_object_get(jcfg, cfgtag);
+ if (!json_is_string(elem)) {
+ fprintf(stderr,
+ "ERROR configuration %s: tag %s is not a string\n",
+ cfgname, cfgtag);
+ exit(2);
+ }
+ name = json_string_value(elem);
+ if (!name) {
+ fprintf(stderr, "ERROR configuration %s: tag %s has no value\n",
+ cfgname, cfgtag);
+ exit(2);
+ }
+ tmp = strdup(name);
+ if (!tmp) {
+ fprintf(stderr, "ERROR no core\n");
+ exit(1);
+ }
+ *cfgval = tmp;
+}
+
+static size_t api_wcb(void *ptr, size_t bsz, size_t nmemb, void *arg)
+{
+ struct api_buf *bp = arg;
+ char *mem;
+ size_t len;
+
+ if (bp->alloc - bp->used < nmemb) {
+ len = (((bp->used + nmemb) / 2000) + 1) * 2000;
+ mem = realloc(bp->buf, len);
+ if (!mem)
+ return 0;
+ bp->buf = mem;
+ bp->alloc = len;
+ }
+ memcpy(bp->buf + bp->used, ptr, nmemb);
+ bp->used += nmemb;
+ return nmemb;
+}
+
+static void apipaths(struct api_conn *connection, struct curl_slist *headers,
+ char *path, char **psd, char **pdc)
+{
+ CURL *curl = connection->curl;
+ struct api_buf apib;
+ char *url;
+ xmlDocPtr doc;
+ xmlNode *etroot;
+ xmlNode *et;
+ xmlChar *reltype;
+ xmlChar *s;
+ char *pathsd = NULL, *pathdc = NULL;
+ CURLcode rcc;
+ int rc;
+
+ rc = asprintf(&url, "%s%s", connection->base, path);
+ if (rc < 0)
+ goto err_alloc;
+
+ memset(&apib, 0, sizeof(struct api_buf));
+ apib.buf = malloc(4000);
+ if (!apib.buf)
+ goto err_alloc;
+ apib.alloc = 4000;
+
+ // if (debugging) /* if (verbose) */
+ // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, api_wcb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &apib);
+
+ rcc = curl_easy_perform(curl);
+ if (rcc != CURLE_OK) {
+ fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
+ exit(1);
+ }
+
+ doc = xmlReadMemory(apib.buf, apib.used, "rhev-api.xml", NULL, 0);
+ if (doc == NULL) {
+ fprintf(stderr, "ERROR API parse error in `%s'\n", url);
+ exit(1);
+ }
+
+ etroot = xmlDocGetRootElement(doc);
+ if (strcmp((const char *)etroot->name, "api") != 0) {
+ fprintf(stderr, "ERROR API root is `api'\n");
+ exit(1);
+ }
+
+ for (et = etroot->children; et; et = et->next) {
+ if (et->type != XML_ELEMENT_NODE)
+ continue;
+ if (strcmp((const char *)et->name, "link") != 0)
+ continue;
+ reltype = xmlGetProp(et, (xmlChar *)"rel");
+ if (!reltype)
+ continue;
+ if (strcmp((char *)reltype, "storagedomains") == 0) {
+ s = xmlGetProp(et, (xmlChar *)"href");
+ if (!s)
+ goto err_href;
+ free(pathsd);
+ pathsd = strdup((char *)s);
+ if (!pathsd)
+ goto err_alloc;
+ xmlFree(s);
+ } else if (strcmp((char *)reltype, "datacenters") == 0) {
+ s = xmlGetProp(et, (xmlChar *)"href");
+ if (!s)
+ goto err_href;
+ free(pathdc);
+ pathdc = strdup((char *)s);
+ if (!pathdc)
+ goto err_alloc;
+ xmlFree(s);
+ }
+ xmlFree(reltype);
+ }
+
+ if (!pathsd) {
+ fprintf(stderr, "ERROR API has no `rel=storagedomains'\n");
+ exit(1);
+ }
+ if (!pathdc) {
+ fprintf(stderr, "ERROR API has no `rel=datacenters'\n");
+ exit(1);
+ }
+
+ *psd = pathsd;
+ *pdc = pathdc;
+
+ xmlFreeDoc(doc);
+ /* xmlCleanupParser(); -- hopefuly not necessary */
+ free(apib.buf);
+ free(url);
+ return;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+
+ err_href:
+ fprintf(stderr, "ERROR <link /> with no `href'\n");
+ exit(1);
+}
+
+static xmlNode *xmlGetChild(xmlNode *node, const char *name)
+{
+ xmlNode *et;
+
+ for (et = node->children; et; et = et->next) {
+ if (et->type != XML_ELEMENT_NODE)
+ continue;
+ if (strcmp((const char *)et->name, name) != 0)
+ continue;
+ return et;
+ }
+ return NULL;
+}
+
+static struct stor_dom *apistordom_1(struct config *cfg, xmlChar *uuidsd,
+ xmlNode *etstor)
+{
+ xmlNode *ettext, *etaddr, *etpath;
+ struct stor_dom *sd;
+
+ etaddr = xmlGetChild(etstor, "address");
+ if (!etaddr) {
+ fprintf(stderr,
+ "WARNIING NFS storage domain without address\n");
+ return NULL;
+ }
+ etpath = xmlGetChild(etstor, "path");
+ if (!etpath) {
+ fprintf(stderr,
+ "WARNIING NFS storage domain without path\n");
+ return NULL;
+ }
+
+ /*
+ * XXX canonicalize host in case of e.g. raw IPv4/IPv6 address.
+ */
+ ettext = etaddr->children; /* mysterious indirection */
+ if (!ettext || ettext->type!=XML_TEXT_NODE || !ettext->content)
+ return NULL;
+ if (strcmp((char *)ettext->content, cfg->nfshost) != 0) {
+ /*
+ * Why even print this message? Because we want to do at least
+ * something if configuration is ham-fisted. However, this
+ * will pop up for every server that has 2 NFS storage domains.
+ * For now we cop-out by tagging it "INFO".
+ */
+ fprintf(stderr, "INFO Host `%s' does not match cfg `%s'\n",
+ (char *)ettext->content, cfg->nfshost);
+ return NULL;
+ }
+ ettext = etpath->children; /* mysterious indirection */
+ if (!ettext || ettext->type!=XML_TEXT_NODE || !ettext->content)
+ return NULL;
+ if (strcmp((char *)ettext->content, cfg->nfspath) != 0) {
+ /*
+ * Why even print this message? Because we want to do at least
+ * something if configuration is ham-fisted. However, this
+ * will pop up for every server that has 2 NFS storage domains.
+ * For now we cop-out by tagging it "INFO".
+ */
+ fprintf(stderr, "INFO Host `%s' does not match cfg `%s'\n",
+ (char *)ettext->content, cfg->nfshost);
+ return NULL;
+ }
+
+ if (!(sd = malloc(sizeof(struct stor_dom)))) {
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+ }
+ memset(sd, 0, sizeof(struct stor_dom));
+ sd->uuid = strdup((char *)uuidsd);
+ if (!sd->uuid) {
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+ }
+ sd->address = cfg->nfshost;
+ sd->path = cfg->nfspath;
+
+ return sd;
+}
+
+static struct stor_dom *apistordom(struct config *cfg,
+ struct api_conn *connection, struct curl_slist *headers, char *pathsd)
+{
+ CURL *curl = connection->curl;
+ struct api_buf apib;
+ char *url;
+ struct stor_dom *sd;
+ xmlDocPtr doc;
+ xmlNode *etroot;
+ xmlNode *et;
+ xmlNode *ettype, *etstor, *ettext;
+ xmlChar *uuidsd;
+ CURLcode rcc;
+ int rc;
+
+ // XXX GET /rhevm-api/storagedomains/ crashes server, see bz#670397
+ // So for now we ignore the pathsd and use a query instead.
+ // rc = asprintf(&url, "%s%s", connection->base, pathsd);
+ rc = asprintf(&url, "%s%s", connection->base, "/rhevm-api/storagedomains/?search=type%20%21%3D%20fcp");
+ if (rc < 0)
+ goto err_alloc;
+
+ memset(&apib, 0, sizeof(struct api_buf));
+ apib.buf = malloc(4000);
+ if (!apib.buf)
+ goto err_alloc;
+ apib.alloc = 4000;
+
+ // if (debugging) /* if (verbose) */
+ // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, api_wcb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &apib);
+
+ rcc = curl_easy_perform(curl);
+ if (rcc != CURLE_OK) {
+ fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
+ exit(1);
+ }
+
+ doc = xmlReadMemory(apib.buf, apib.used, "rhev-api.xml", NULL, 0);
+ if (doc == NULL) {
+ fprintf(stderr, "ERROR API parse error in `%s'\n", url);
+ exit(1);
+ }
+
+ etroot = xmlDocGetRootElement(doc);
+ if (strcmp((const char *)etroot->name, "storage_domains") != 0) {
+ fprintf(stderr, "ERROR API root is `storage_domains'\n");
+ exit(1);
+ }
+
+ sd = NULL;
+ for (et = etroot->children; et; et = et->next) {
+ if (et->type != XML_ELEMENT_NODE)
+ continue;
+ if (strcmp((const char *)et->name, "storage_domain") != 0)
+ continue;
+
+ ettype = xmlGetChild(et, "type");
+ if (!ettype)
+ continue;
+ ettext = ettype->children; /* mysterious indirection */
+ if (!ettext || ettext->type!=XML_TEXT_NODE || !ettext->content)
+ continue;
+ if (strcmp((char *)ettext->content, "EXPORT") != 0)
+ continue;
+
+ etstor = xmlGetChild(et, "storage");
+ if (!etstor)
+ continue;
+
+ ettype = xmlGetChild(etstor, "type");
+ if (!ettype)
+ continue;
+ ettext = ettype->children; /* mysterious indirection */
+ if (!ettext || ettext->type!=XML_TEXT_NODE || !ettext->content)
+ continue;
+ if (strcmp((char *)ettext->content, "NFS") != 0)
+ continue;
+
+ uuidsd = xmlGetProp(et, (xmlChar *)"id");
+ if (!uuidsd) {
+ fprintf(stderr,
+ "WARNING NFS storage domain without UUID\n");
+ continue;
+ }
+ sd = apistordom_1(cfg, uuidsd, etstor);
+ xmlFree(uuidsd);
+ if (sd)
+ break;
+ }
+
+ if (!sd) {
+ fprintf(stderr, "ERROR NFS storage domain for `%s' not found\n",
+ url);
+ exit(1);
+ }
+
+ xmlFreeDoc(doc);
+ /* xmlCleanupParser(); -- hopefuly not necessary */
+ free(apib.buf);
+ free(url);
+ return sd;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+ return NULL;
+}
+
+static int apipoolid(struct config *cfg, struct api_conn *connection,
+ struct curl_slist *headers, xmlChar *pathsds, char *sd_uuid)
+{
+ CURL *curl = connection->curl;
+ struct api_buf apib;
+ char *url;
+ xmlDocPtr doc;
+ xmlNode *etsd;
+ xmlNode *etroot;
+ xmlChar *xid;
+ CURLcode rcc;
+ int rc;
+
+ rc = asprintf(&url, "%s%s", connection->base, pathsds);
+ if (rc < 0)
+ goto err_alloc;
+
+ memset(&apib, 0, sizeof(struct api_buf));
+ apib.buf = malloc(4000);
+ if (!apib.buf)
+ goto err_alloc;
+ apib.alloc = 4000;
+
+ // if (debugging) /* if (verbose) */
+ // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, api_wcb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &apib);
+
+ rcc = curl_easy_perform(curl);
+ if (rcc != CURLE_OK) {
+ fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
+ exit(1);
+ }
+
+ doc = xmlReadMemory(apib.buf, apib.used, "rhev-api.xml", NULL, 0);
+ if (doc == NULL) {
+ fprintf(stderr, "ERROR API parse error in `%s'\n", url);
+ exit(1);
+ }
+
+ etroot = xmlDocGetRootElement(doc);
+ if (strcmp((const char *)etroot->name, "storage_domains") != 0) {
+ fprintf(stderr, "ERROR API root is `storage_domains'\n");
+ exit(1);
+ }
+
+ for (etsd = etroot->children; etsd; etsd = etsd->next) {
+ if (etsd->type != XML_ELEMENT_NODE)
+ continue;
+ if (strcmp((const char *)etsd->name, "storage_domain") != 0)
+ continue;
+
+ xid = xmlGetProp(etsd, (xmlChar *)"id");
+ if (!xid) {
+ fprintf(stderr,
+ "WARNING storage domain without UUID\n");
+ continue;
+ }
+ if (strcmp((char *)xid, sd_uuid) == 0) {
+ xmlFree(xid);
+ rc = 1;
+ goto ret;
+ }
+ xmlFree(xid);
+ }
+
+ rc = 0;
+ret:
+ xmlFreeDoc(doc);
+ /* xmlCleanupParser(); -- hopefuly not necessary */
+ free(apib.buf);
+ free(url);
+ return rc;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+ return 0;
+}
+
+static char *apipool(struct config *cfg, struct api_conn *connection,
+ struct curl_slist *headers, char *pathdc, char *sd_uuid)
+{
+ CURL *curl = connection->curl;
+ struct api_buf apib;
+ char *url;
+ char *ret;
+ xmlDocPtr doc;
+ xmlNode *etroot;
+ xmlNode *etdc, *etlink;
+ // xmlNode *ettype, *etstor, *ettext;
+ xmlChar *uuiddc;
+ xmlChar *rel, *href;
+ CURLcode rcc;
+ int rc;
+
+ rc = asprintf(&url, "%s%s", connection->base, pathdc);
+ if (rc < 0)
+ goto err_alloc;
+
+ memset(&apib, 0, sizeof(struct api_buf));
+ apib.buf = malloc(4000);
+ if (!apib.buf)
+ goto err_alloc;
+ apib.alloc = 4000;
+
+ // if (debugging) /* if (verbose) */
+ // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, api_wcb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &apib);
+
+ rcc = curl_easy_perform(curl);
+ if (rcc != CURLE_OK) {
+ fprintf(stderr, "ERROR curl failed GET url `%s'\n", url);
+ exit(1);
+ }
+
+ /* <--- cleanup curl connection here if needed */
+
+ doc = xmlReadMemory(apib.buf, apib.used, "rhev-api-dc.xml", NULL, 0);
+ if (doc == NULL) {
+ fprintf(stderr, "ERROR API parse error in `%s'\n", url);
+ exit(1);
+ }
+
+ etroot = xmlDocGetRootElement(doc);
+ if (strcmp((const char *)etroot->name, "data_centers") != 0) {
+ fprintf(stderr, "ERROR API root is `data_centers'\n");
+ exit(1);
+ }
+
+ for (etdc = etroot->children; etdc; etdc = etdc->next) {
+ if (etdc->type != XML_ELEMENT_NODE)
+ continue;
+ uuiddc = xmlGetProp(etdc, (xmlChar *)"id");
+ if (!uuiddc) {
+ fprintf(stderr,
+ "WARNING NFS storage domain without UUID\n");
+ continue;
+ }
+ for (etlink = etdc->children; etlink; etlink = etlink->next) {
+ if (etlink->type != XML_ELEMENT_NODE)
+ continue;
+ /* most likely it's not even a link, but "name" or "version" */
+ if (strcmp((const char *)etlink->name, "link") != 0)
+ continue;
+ rel = xmlGetProp(etlink, (xmlChar *)"rel");
+ if (!rel)
+ continue;
+ /* there's a bunch of other links like "files", "permissions" */
+ if (strcmp((const char *)rel, "storagedomains") != 0) {
+ xmlFree(rel);
+ continue;
+ }
+ href = xmlGetProp(etlink, (xmlChar *)"href");
+ if (!href) {
+ fprintf(stderr,
+ "WARNING data canter link without href\n");
+ xmlFree(rel);
+ continue;
+ }
+
+ if (apipoolid(cfg, connection, headers, href, sd_uuid)) {
+ /* StoragePoolId is Data Center's UUID */
+
+ ret = strdup((char *)uuiddc);
+ if (!ret)
+ goto err_alloc;
+ xmlFree(href);
+ xmlFree(rel);
+
+ xmlFree(uuiddc);
+ xmlFreeDoc(doc);
+ /* xmlCleanupParser(); -- hopefuly not necessary */
+ free(apib.buf);
+ free(url);
+ return ret;
+ }
+ xmlFree(href);
+ xmlFree(rel);
+ }
+ xmlFree(uuiddc);
+ }
+
+ fprintf(stderr, "ERROR pool not found for storage domain %s\n", sd_uuid);
+ exit(1);
+ return NULL;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+ return NULL;
+}
+
+static struct stor_dom *apistart(struct config *cfg)
+{
+ struct http_uri huri;
+ struct stor_dom *sd;
+ char *host, *path, *base;
+ char *pathsd, *pathdc;
+ char *authraw, *authhdr;
+ size_t authrlen, authhlen;
+ struct curl_slist *headers, *h;
+ struct api_conn connection;
+ int rc;
+
+ /*
+ * Note that using a proper URL parser means that we drop query and
+ * fragment from the configured URL. Fortunately, it is never a factor
+ * in RHEV-M API, due to its RESTful nature.
+ */
+ if (!huri_parse(&huri, cfg->apiurl)) {
+ fprintf(stderr, "ERROR unable to parse `apiurl': `%s'\n",
+ cfg->apiurl);
+ exit(1);
+ }
+
+ path = strndup(huri.path, huri.path_len);
+ if (!path)
+ goto err_alloc;
+ host = strndup(huri.hostname, huri.hostname_len);
+ if (!host)
+ goto err_alloc;
+
+ if (huri.port)
+ rc = asprintf(&base, "http://%s:%u", host, huri.port);
+ else
+ rc = asprintf(&base, "http://%s", host);
+ if (rc < 0)
+ goto err_alloc;
+
+ connection.base = base;
+ connection.curl = curl_easy_init();
+
+ headers = NULL;
+
+ rc = asprintf(&authraw, "%s:%s", cfg->apiuser, cfg->apipass);
+ if (rc < 0)
+ goto err_alloc;
+ authrlen = strlen(authraw);
+ authhlen = ((authrlen+2)/3) * 4; /* base64 expands 3 into 4 */
+ authhlen += sizeof("Authorization: Basic ")-1;
+ authhdr = malloc(authhlen + 3); /* \r\n and nul */
+ if (!authhdr)
+ goto err_alloc;
+ strcpy(authhdr, "Authorization: Basic ");
+ base64_encode(authraw, authrlen,
+ authhdr + (sizeof("Authorization: Basic ")-1),
+ authhlen - (sizeof("Authorization: Basic ")-1));
+ strcat(authhdr, "\r\n");
+
+ h = curl_slist_append(headers, authhdr);
+ if (!h)
+ goto err_alloc;
+ headers = h;
+
+ /*
+ * Step 1, fetch the API root
+ *
+ * This is largely a formality, because if we know names of resources,
+ * why not to fetch resources directly? But the docs imply we should
+ * jump through this hoop.
+ */
+ apipaths(&connection, headers, path, &pathsd, &pathdc);
+
+ /*
+ * We should pull the API version and check that it's more than 2.3 XXX
+ *
+ * Step 2, connect again, get domains
+ */
+ sd = apistordom(cfg, &connection, headers, pathsd);
+
+ /*
+ * Step 3, find the "storage pool ID"
+ *
+ * This is crazy to be a separate step. You'd expect the storage domain
+ * descriptor itself contain its pool, to be saved on Step 2. But no.
+ */
+ sd->poolid = apipool(cfg, &connection, headers, pathdc, sd->uuid);
+
+ curl_easy_cleanup(connection.curl);
+
+ free(authhdr);
+ free(authraw);
+ free(host);
+ free(path);
+ free(base);
+ return sd;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+}
+
+static void spitovf(struct config *cfg, struct stor_dom *sd,
+ uuid_t img_uuid, uuid_t vol_uuid, off_t vol_size, uuid_t tpl_uuid)
+{
+ time_t now;
+ struct tm now_tm;
+ char now_str[50];
+ xmlTextWriterPtr writer;
+ char uuidbuf[37];
+ char buf100[100];
+ char *s;
+ char *domdir, *tmpovfdir, *tmpovf, *tmpimgdir, *imgdir, *ovfdir;
+ int rc;
+
+ uuid_generate_random(tpl_uuid);
+
+ rc = asprintf(&domdir, "%s/%s", cfg->nfsdir, sd->uuid);
+ if (rc < 0)
+ goto err_alloc;
+ uuid_unparse_lower(tpl_uuid, uuidbuf);
+ rc = asprintf(&tmpovfdir, "%s/iwhd.%s", domdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+ uuid_unparse_lower(tpl_uuid, uuidbuf);
+ rc = asprintf(&tmpovf, "%s/%s.ovf", tmpovfdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+ if (mkdir(tmpovfdir, 0770) < 0) {
+ fprintf(stderr, "ERROR Failed to make directory %s: %s\n",
+ tmpovfdir, strerror(errno));
+ exit(1);
+ }
+
+ uuid_unparse_lower(img_uuid, uuidbuf);
+ rc = asprintf(&tmpimgdir, "%s/iwhd.%s", domdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+ uuid_unparse_lower(img_uuid, uuidbuf);
+ rc = asprintf(&imgdir, "%s/images/%s", domdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+
+ uuid_unparse_lower(tpl_uuid, uuidbuf);
+ rc = asprintf(&ovfdir, "%s/master/vms/%s", domdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+
+ now = time(NULL);
+ gmtime_r(&now, &now_tm);
+ strftime(now_str, 50, "%Y/%m/%d %H:%M:%S", &now_tm);
+
+ writer = xmlNewTextWriterFilename(tmpovf, 0);
+ if (!writer) {
+ fprintf(stderr, "ERROR Error creating the xml driver for %s\n",
+ tmpovf);
+ exit(1);
+ }
+
+ rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
+ if (rc < 0) {
+ fprintf(stderr, "ERROR Error in xmlTextWriterStartDocument\n");
+ exit(1);
+ }
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "ovf:Envelope");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:version", BAD_CAST "0.9");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xmlns:ovf",
+ BAD_CAST "http://schemas.dmtf.org/ovf/envelope/1/");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xmlns:rasd",
+ BAD_CAST "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationS...");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xmlns:vssd",
+ BAD_CAST "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettin...");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xmlns:xsi",
+ BAD_CAST "http://www.w3.org/2001/XMLSchema-instance");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "References");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "File");
+ if (rc < 0) goto err_xml;
+ uuid_unparse_lower(img_uuid, buf100);
+ buf100[36] = '/';
+ uuid_unparse_lower(vol_uuid, buf100+37);
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:href", BAD_CAST buf100);
+ if (rc < 0) goto err_xml;
+ uuid_unparse_lower(vol_uuid, uuidbuf);
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:id", BAD_CAST uuidbuf);
+ if (rc < 0) goto err_xml;
+ snprintf(buf100, 100, "%llu", (long long) vol_size);
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:size", BAD_CAST buf100);
+ if (rc < 0) goto err_xml;
+ /* This is basename(), but we handroll it to make sure, due to BSD. */
+ if (!(s = strrchr(cfg->image, '/')) || s[1]==0)
+ s = cfg->image;
+ else
+ s++;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:description", BAD_CAST s);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterEndElement(writer); /* close <File> */
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterEndElement(writer); /* close <References> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Section");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xsi:type", BAD_CAST "ovf:NetworkSection_Type");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "Info", BAD_CAST "List of Networks");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterEndElement(writer); /* close <Section> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Section");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xsi:type", BAD_CAST "ovf:DiskSection_Type");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Disk");
+ if (rc < 0) goto err_xml;
+ uuid_unparse_lower(vol_uuid, uuidbuf);
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:diskId", BAD_CAST uuidbuf);
+ if (rc < 0) goto err_xml;
+ snprintf(buf100, 100, "%llu",
+ (long long)((vol_size + (1024*1024*1024) - 1) / (1024*1024*1024)));
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:size", BAD_CAST buf100);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:actual_size", BAD_CAST buf100);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:vm_snapshot_id", (const xmlChar *) NULID);
+ if (rc < 0) goto err_xml;
+ uuid_unparse_lower(img_uuid, buf100);
+ buf100[36] = '/';
+ uuid_unparse_lower(vol_uuid, buf100+37);
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:fileRef", BAD_CAST buf100);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:format",
+ BAD_CAST "http://www.vmware.com/technical-resources/interfaces/vmdk_access.html");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:volume-format", BAD_CAST "RAW");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:volume-type", BAD_CAST "Preallocated");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:disk-interface", BAD_CAST "VirtIO");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:disk-type", BAD_CAST "System");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:boot", BAD_CAST "true");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:wipe-after-delete", BAD_CAST "false");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterEndElement(writer); /* close <Disk> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Section> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Content");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xsi:type", BAD_CAST "ovf:VirtualSystem_Type");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:id", BAD_CAST "out");
+ if (rc < 0) goto err_xml;
+
+ if (!(s = strrchr(cfg->image, '/'))) s = cfg->image;
+ rc = xmlTextWriterWriteElement(writer, BAD_CAST "Name", BAD_CAST s);
+ if (rc < 0) goto err_xml;
+
+ uuid_unparse_lower(tpl_uuid, uuidbuf);
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "TemplateId", BAD_CAST uuidbuf);
+ if (rc < 0) goto err_xml;
+ /* spec also has 'TemplateName' */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "Description", BAD_CAST "Template by iwhd");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "Domain", BAD_CAST "");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "CreationDate", BAD_CAST now_str);
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "TimeZone", BAD_CAST "");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "IsAutoSuspend", BAD_CAST "false");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "VmType", BAD_CAST "1");
+ if (rc < 0) goto err_xml;
+
+ /* vnc = 0, gxl = 1 */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "default_display_type", BAD_CAST "0");
+ if (rc < 0) goto err_xml;
+
+ /*
+ * C=0, DC=1, N=2, CDN=3, CND=4, DCN=5, DNC=6, NCD=7,
+ * NDC=8, CD=9, D=10, CN=11, DN=12, NC=13, ND=14
+ * (C - HardDisk, D - CDROM, N - Network)
+ */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "default_boot_sequence", BAD_CAST "1");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Section");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xsi:type", BAD_CAST "ovf:OperatingSystemSection_Type");
+ if (rc < 0) goto err_xml;
+ uuid_unparse_lower(tpl_uuid, uuidbuf);
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:id", BAD_CAST uuidbuf);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "ovf:required", BAD_CAST "false");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "Info", BAD_CAST "Guest OS");
+ if (rc < 0) goto err_xml;
+ /* This is rigid, must be "Other", "OtherLinux", "RHEL6", or such. */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "Description", BAD_CAST "OtherLinux");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterEndElement(writer); /* close <Section> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Section");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST "xsi:type", BAD_CAST "ovf:VirtualHardwareSection_Type");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "Info", BAD_CAST "1 CPU, 512 Memory");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "System");
+ if (rc < 0) goto err_xml;
+ /* This is probably wrong, needs actual type. */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "vssd:VirtualSystemType", BAD_CAST "RHEVM 4.6.0.163");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterEndElement(writer); /* close <System> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Item");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Caption", BAD_CAST "1 virtual CPU");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Description", BAD_CAST "Number of virtual CPU");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:InstanceId", BAD_CAST "1");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ResourceType", BAD_CAST "3");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:num_of_sockets", BAD_CAST "1");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:cpu_per_socket", BAD_CAST "1");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Item> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Item");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Caption", BAD_CAST "512 MB of memory");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Description", BAD_CAST "Memory Size");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:InstanceId", BAD_CAST "2");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ResourceType", BAD_CAST "4");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:AllocationUnits", BAD_CAST "MegaBytes");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:VirtualQuantity", BAD_CAST "512");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Item> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Item");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Caption", BAD_CAST "Drive 1");
+ if (rc < 0) goto err_xml;
+ /* Why no rasd:Description for disks? */
+ uuid_unparse_lower(vol_uuid, uuidbuf);
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:InstanceId", BAD_CAST uuidbuf);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ResourceType", BAD_CAST "17");
+ if (rc < 0) goto err_xml;
+ uuid_unparse_lower(img_uuid, buf100);
+ buf100[36] = '/';
+ uuid_unparse_lower(vol_uuid, buf100+37);
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:HostResource", BAD_CAST buf100);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Parent",
+ (const xmlChar *) NULID);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Template",
+ (const xmlChar *) NULID);
+ if (rc < 0) goto err_xml;
+ /* List of installed applications, separated by comma */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ApplicationList", BAD_CAST "");
+ if (rc < 0) goto err_xml;
+ /*
+ * "Storage Domain Id"
+ * This corresponds to ID of volgroup in host where snapshot was taken.
+ * Obviously we have nothing like it.
+ */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:StorageId",
+ (const xmlChar *) NULID);
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:StoragePoolId", BAD_CAST sd->poolid);
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:CreationDate", BAD_CAST now_str);
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:LastModified", BAD_CAST now_str);
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Item> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Item");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Caption", BAD_CAST "Ethernet 0 rhevm");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:InstanceId", BAD_CAST "3");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ResourceType", BAD_CAST "10");
+ if (rc < 0) goto err_xml;
+ /* e1000 = 2, pv = 3 */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ResourceSubType", BAD_CAST "3");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Connection", BAD_CAST "rhevm");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Name", BAD_CAST "eth0");
+ if (rc < 0) goto err_xml;
+
+ /* also allowed is "MACAddress" */
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:speed", BAD_CAST "1000");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Item> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterStartElement(writer, BAD_CAST "Item");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:Caption", BAD_CAST "Graphics");
+ if (rc < 0) goto err_xml;
+ /* doc says "6", reality is "5" */
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:InstanceId", BAD_CAST "5");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:ResourceType", BAD_CAST "20");
+ if (rc < 0) goto err_xml;
+ rc = xmlTextWriterWriteElement(writer,
+ BAD_CAST "rasd:VirtualQuantity", BAD_CAST "1");
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Item> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Section> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndElement(writer); /* close <Content> */
+ if (rc < 0) goto err_xml;
+
+ rc = xmlTextWriterEndDocument(writer);
+ if (rc < 0) {
+ fprintf(stderr, "ERROR Error in xmlTextWriterEndDocument\n");
+ exit(1);
+ }
+
+ xmlFreeTextWriter(writer);
+
+ if (rename(tmpimgdir, imgdir) < 0) {
+ fprintf(stderr, "ERROR Failed to rename from %s to %s: %s\n",
+ tmpimgdir, imgdir, strerror(errno));
+ exit(1);
+ }
+ if (rename(tmpovfdir, ovfdir) < 0) {
+ fprintf(stderr, "ERROR Failed to rename from %s to %s: %s\n",
+ tmpovfdir, ovfdir, strerror(errno));
+ exit(1);
+ }
+
+ free(domdir);
+ free(tmpovfdir);
+ free(tmpovf);
+ free(tmpimgdir);
+ free(imgdir);
+ free(ovfdir);
+ return;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+
+ err_xml:
+ fprintf(stderr, "ERROR Failed to form XML\n");
+ exit(1);
+}
+
+static void copyimage(struct config *cfg, struct stor_dom *sd, uuid_t tpl_uuid)
+{
+ int rc;
+ struct stat statb;
+ uuid_t vol_uuid, img_uuid;
+ char *domdir, *tmpimgdir;
+ char *imgsrc, *imgdst, *imgmeta;
+ char uuidbuf[37];
+ time_t now;
+ off_t vol_size;
+ FILE *fp;
+
+ if (geteuid() == 0) {
+ /* printf("We're root, changing user and group to 36"); */
+ setregid(-1, NFSGID);
+ setreuid(-1, NFSUID);
+ } else {
+ if (geteuid() != NFSUID || getegid() != NFSGID) {
+ fprintf(stderr,
+ "ERROR Have to run with user and group 36\n");
+ exit(1);
+ }
+ }
+
+ rc = asprintf(&domdir, "%s/%s", cfg->nfsdir, sd->uuid);
+ if (rc < 0)
+ goto err_alloc;
+ if (stat(domdir, &statb) < 0) {
+ fprintf(stderr, "ERROR failed to stat %s: %s\n",
+ domdir, strerror(errno));
+ exit(1);
+ }
+ if (!S_ISDIR(statb.st_mode)) {
+ fprintf(stderr, "ERROR path %s is not a directory\n", domdir);
+ exit(1);
+ }
+
+ uuid_generate_random(vol_uuid);
+ uuid_generate_random(img_uuid);
+
+ /* XXX Do something about garbage-collecting iwhd.* directories */
+
+ uuid_unparse_lower(img_uuid, uuidbuf);
+ rc = asprintf(&tmpimgdir, "%s/iwhd.%s", domdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+ if (mkdir(tmpimgdir, 0770) < 0) {
+ fprintf(stderr, "ERROR Failed to make directory %s: %s\n",
+ tmpimgdir, strerror(errno));
+ exit(1);
+ }
+
+ now = time(NULL);
+
+ imgsrc = cfg->image;
+ uuid_unparse_lower(vol_uuid, uuidbuf);
+ rc = asprintf(&imgdst, "%s/%s", tmpimgdir, uuidbuf);
+ if (rc < 0)
+ goto err_alloc;
+
+ /* No error processing here, it exits on error. */
+ copy_file_preserving(imgsrc, imgdst);
+
+ if (stat(imgdst, &statb) < 0) {
+ fprintf(stderr, "ERROR failed to stat %s: %s\n",
+ imgdst, strerror(errno));
+ exit(1);
+ }
+ vol_size = statb.st_size;
+
+ rc = asprintf(&imgmeta, "%s.meta", imgdst);
+ if (rc < 0)
+ goto err_alloc;
+
+ fp = fopen(imgmeta, "w");
+ if (!fp) {
+ fprintf(stderr, "ERROR Failed to create %s: %s\n",
+ imgmeta, strerror(errno));
+ exit(1);
+ }
+ fprintf(fp, "DOMAIN=%s\n", sd->uuid);
+ /* saved template has VOLTYPE=SHARED */
+ fprintf(fp, "VOLTYPE=LEAF\n");
+ fprintf(fp, "CTIME=%llu\n", (long long)now);
+ /* saved template has FORMAT=COW */
+ fprintf(fp, "FORMAT=RAW\n");
+ uuid_unparse_lower(img_uuid, uuidbuf);
+ fprintf(fp, "IMAGE=%s\n", uuidbuf);
+ fprintf(fp, "DISKTYPE=1\n");
+ fprintf(fp, "PUUID=%s\n", NULID);
+ fprintf(fp, "LEGALITY=LEGAL\n");
+ fprintf(fp, "MTIME=%llu\n", (long long)now);
+ fprintf(fp, "POOL_UUID=%s\n", sd->poolid);
+ /* assuming 1KB alignment, 512 is good for sure */
+ fprintf(fp, "SIZE=%llu\n", (long long) (vol_size/512));
+ fprintf(fp, "TYPE=SPARSE\n");
+ fprintf(fp, "DESCRIPTION=Uploaded by iwhd+rhevreg\n");
+ fprintf(fp, "EOF\n");
+ if (close_stream(fp)) {
+ fprintf(stderr, "ERROR Failed to write %s\n", imgmeta);
+ exit(1);
+ }
+
+ spitovf(cfg, sd, img_uuid, vol_uuid, vol_size, tpl_uuid);
+
+ free(imgmeta);
+ free(imgdst);
+ free(tmpimgdir);
+ free(domdir);
+ return;
+
+ err_alloc:
+ fprintf(stderr, "ERROR No core\n");
+ exit(1);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ uuid_t tpl_uuid;
+ json_error_t err;
+ char *cfgname;
+ struct config cfg;
+ json_t *jcfg;
+ struct stor_dom *sd;
+ char uuidbuf[37];
+
+ env_p = envp;
+
+ if (argc == 1) {
+ cfgname = TAG ".conf";
+ } else if (argc == 2) {
+ cfgname = argv[1];
+ if (cfgname[0] == 0 || cfgname[0] == '-')
+ Usage();
+ } else {
+ Usage();
+ return 2; /* gcc warning about cfgname initialization */
+ }
+
+ memset(&cfg, 0, sizeof(struct config));
+
+ jcfg = json_load_file(cfgname, &err);
+ if (!jcfg) {
+ fprintf(stderr, "ERROR configuration JSON error %s:%d: %s\n",
+ cfgname, err.line, err.text);
+ exit(2);
+ }
+
+ if (json_typeof(jcfg) != JSON_OBJECT) {
+ fprintf(stderr,
+ "ERROR configuration JSON %s: root is not a struct\n",
+ cfgname);
+ exit(2);
+ }
+
+ /* image: local filename with disk image */
+ cfg_veripick(&cfg.image, cfgname, jcfg, "image");
+ if (!path_exists(cfg.image)) {
+ fprintf(stderr, "ERROR image %s does not exist\n", cfg.image);
+ exit(1);
+ }
+ /* apiurl: the so-called "base", usually "rhev-api" */
+ cfg_veripick(&cfg.apiurl, cfgname, jcfg, "apiurl");
+ /*
+ * apiuser: username(a)AD.domain
+ * We do not enforce with '@' syntax in case someone comes up with
+ * RHEV-M that takes local authentication of some other trick.
+ */
+ cfg_veripick(&cfg.apiuser, cfgname, jcfg, "apiuser");
+ /* apipass: password for apiuser */
+ cfg_veripick(&cfg.apipass, cfgname, jcfg, "apipass");
+ /*
+ * nfshost: NFS server name
+ * nfspath: export path
+ * All RHEV-M servers have several storage domains, so we use this to
+ * select one. Also, we verify if 'nfsdir' points where it should.
+ */
+ cfg_veripick(&cfg.nfshost, cfgname, jcfg, "nfshost");
+ cfg_veripick(&cfg.nfspath, cfgname, jcfg, "nfspath");
+ /*
+ * nfsdir: A directory where sysadmin or boot scripts or autofs mounted
+ * the same area that RHEV-M considers an export domain (/mnt/vdsm/rhevm23).
+ * N.B. If iwhd is running on the NFS server itself, nfsdir can be the
+ * exported directory itself (like /home/vdsm/rhevm23). We verify that
+ * the directory contains an expected structure, so attach it to RHEV-M
+ * before trying to register any images.
+ */
+ cfg_veripick(&cfg.nfsdir, cfgname, jcfg, "nfsdir");
+ if (cfg.nfsdir[0] == 0) {
+ fprintf(stderr, "ERROR configuration: `nfsdir' is empty\n");
+ exit(2);
+ }
+ if (cfg.nfsdir[0] != '/') {
+ fprintf(stderr, "ERROR configuration: `nfsdir' is relative\n");
+ exit(2);
+ }
+
+ json_decref(jcfg);
+ jcfg = NULL;
+
+ sd = apistart(&cfg);
+
+ copyimage(&cfg, sd, tpl_uuid);
+
+ uuid_unparse_lower(tpl_uuid, uuidbuf);
+ printf("IMAGE %s\n", uuidbuf);
+ return 0;
+}
diff --git a/gnulib-tests/.gitignore b/gnulib-tests/.gitignore
index 259f316..43fcc40 100644
--- a/gnulib-tests/.gitignore
+++ b/gnulib-tests/.gitignore
@@ -9,6 +9,7 @@
/float+.h
/float.h
/float.in.h
+/ftello.c
/getpagesize.c
/gnulib.mk
/ignore-value.h
@@ -16,6 +17,7 @@
/init.sh
/inttostr.c
/inttostr.h
+/lseek.c
/lstat.c
/macros.h
/malloca.c
@@ -31,12 +33,15 @@
/priv-set.c
/priv-set.h
/putenv.c
+/read-file.c
+/read-file.h
/same-inode.h
/setenv.c
/signature.h
/size_max.h
/snprintf.c
/stat.c
+/stdio-impl.h
/stdio-write.c
/stdio.h
/stdio.in.h
@@ -45,17 +50,28 @@
/sys_stat.h
/sys_stat.in.h
/test-alloca-opt.c
+/test-base64.c
/test-binary-io.c
/test-binary-io.sh
/test-bitrotate.c
/test-c-ctype.c
+/test-copy-acl.c
+/test-copy-acl.sh
+/test-copy-file.c
+/test-copy-file.sh
/test-dirname.c
/test-dup2.c
/test-environ.c
/test-errno.c
/test-fcntl-h.c
+/test-file-has-acl.c
+/test-file-has-acl.sh
/test-fpending.c
/test-fpending.sh
+/test-ftello.c
+/test-ftello.sh
+/test-ftello2.sh
+/test-ftello3.c
/test-getopt.c
/test-getopt.h
/test-getopt_long.h
@@ -64,6 +80,8 @@
/test-ignore-value.c
/test-inttostr.c
/test-inttypes.c
+/test-lseek.c
+/test-lseek.sh
/test-lstat.c
/test-lstat.h
/test-malloc-gnu.c
@@ -82,7 +100,11 @@
/test-priv-set.c
/test-quotearg-simple.c
/test-quotearg.h
+/test-read-file.c
/test-realloc-gnu.c
+/test-sameacls.c
+/test-set-mode-acl.c
+/test-set-mode-acl.sh
/test-setenv.c
/test-snprintf.c
/test-stat.c
diff --git a/iwhd.spec.in b/iwhd.spec.in
index 629dd50..b6ec514 100644
--- a/iwhd.spec.in
+++ b/iwhd.spec.in
@@ -23,6 +23,7 @@ BuildRequires: jansson-devel
BuildRequires: libcurl-devel
BuildRequires: libmicrohttpd-devel
BuildRequires: libxml2-devel
+BuildRequires: libuuid-devel
BuildRequires: mongodb-devel
BuildRequires: bison
BuildRequires: flex
diff --git a/lib/.gitignore b/lib/.gitignore
index bf5c36c..f7907b8 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -1,6 +1,12 @@
+/acl-internal.h
+/acl.h
+/acl_entries.c
/arg-nonnull.h
+/base64.c
+/base64.h
/basename-lgpl.c
/basename.c
+/binary-io.h
/bitrotate.h
/c++defs.h
/c-ctype.c
@@ -16,6 +22,9 @@
/closeout.h
/config.charset
/configmake.h
+/copy-acl.c
+/copy-file.c
+/copy-file.h
/dirname-lgpl.c
/dirname.c
/dirname.h
@@ -27,8 +36,13 @@
/exitfail.c
/exitfail.h
/fclose.c
+/fcntl.h
+/fcntl.in.h
+/file-has-acl.c
/fpending.c
/fpending.h
+/full-write.c
+/full-write.h
/getopt.c
/getopt.h
/getopt.in.h
@@ -55,6 +69,7 @@
/memchr.c
/memchr.valgrind
/mkstemp.c
+/open.c
/pipe.c
/progname.c
/progname.h
@@ -67,6 +82,11 @@
/ref-add.sin
/ref-del.sed
/ref-del.sin
+/safe-read.c
+/safe-read.h
+/safe-write.c
+/safe-write.h
+/set-mode-acl.c
/stat.c
/stdarg.h
/stdarg.in.h
@@ -121,6 +141,7 @@
/wchar.in.h
/wctype.h
/wctype.in.h
+/write.c
/xalloc-die.c
/xalloc.h
/xmalloc.c
diff --git a/setup.c b/setup.c
index 3f35355..13b9943 100644
--- a/setup.c
+++ b/setup.c
@@ -65,6 +65,7 @@ extern backend_func_tbl s3_func_tbl;
extern backend_func_tbl curl_func_tbl;
extern backend_func_tbl cf_func_tbl;
extern backend_func_tbl fs_func_tbl;
+extern backend_func_tbl fs_rhevm_func_tbl;
static json_t *config = NULL;
static Hash_table *prov_hash = NULL;
@@ -210,7 +211,7 @@ json_validate_server (unsigned int i)
else if (!strcasecmp(type,"http")) {
needs = NEED_SERVER;
}
- else if (!strcasecmp(type,"fs")) {
+ else if (!strcasecmp(type,"fs") || !strcasecmp(type,"fs-rhev-m")) {
needs = NEED_PATH;
}
else {
@@ -328,6 +329,9 @@ convert_provider (int i, provider_t *out)
else if (!strcasecmp(out->type,"fs")) {
out->func_tbl = &fs_func_tbl;
}
+ else if (!strcasecmp(out->type,"fs-rhev-m")) {
+ out->func_tbl = &fs_rhevm_func_tbl;
+ }
else {
out->func_tbl = &bad_func_tbl;
}
@@ -417,6 +421,8 @@ add_provider (Hash_table *h)
prov->func_tbl = &cf_func_tbl;
else if (!strcasecmp(prov->type,"fs"))
prov->func_tbl = &fs_func_tbl;
+ else if (!strcasecmp(prov->type,"fs-rhev-m"))
+ prov->func_tbl = &fs_rhevm_func_tbl;
else
prov->func_tbl = &bad_func_tbl;
13 years, 2 months
bootstrap.conf lib/.gitignore rest.c
by Jim Meyering
bootstrap.conf | 1 +
lib/.gitignore | 2 ++
rest.c | 6 ++++++
3 files changed, 9 insertions(+)
New commits:
commit 0a1a0fc83ce20350cb76452b9f520260011e5e20
Author: Jim Meyering <meyering(a)redhat.com>
Date: Thu Mar 3 20:11:56 2011 +0100
diagnose excess arguments
Before, adding a stray "localhost:9001" without a preceding -d
would be silently accepted, yet that last argument would be
fatally ignored. Now, iwhd diagnoses this.
* rest.c: Include "quote.h"
(main): Diagnose excess arguments.
* bootstrap.conf (gnulib_modules): Add quote.
diff --git a/bootstrap.conf b/bootstrap.conf
index cf1da8b..2e4cf7f 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -40,6 +40,7 @@ manywarnings
mkstemp
pipe-posix
progname
+quote
quotearg
realloc-gnu
ssize_t
diff --git a/lib/.gitignore b/lib/.gitignore
index af6d24d..bf5c36c 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -58,6 +58,8 @@
/pipe.c
/progname.c
/progname.h
+/quote.c
+/quote.h
/quotearg.c
/quotearg.h
/realloc.c
diff --git a/rest.c b/rest.c
index 3192915..e89e140 100644
--- a/rest.c
+++ b/rest.c
@@ -42,6 +42,7 @@
#include "meta.h"
#include "backend.h"
#include "setup.h"
+#include "quote.h"
#include "replica.h"
#include "template.h"
#include "mpipe.h"
@@ -2249,6 +2250,11 @@ main (int argc, char **argv)
}
args_done:
+ if (optind < argc) {
+ error (0, 0, _("extra operand %s"), quote (argv[optind]));
+ usage (EXIT_FAILURE);
+ }
+
if (!db_port) {
db_port = autostart ? AUTO_MONGOD_PORT : 27017;
}
13 years, 2 months