esx_util.c 17.1 KB
Newer Older
1 2

/*
3
 * esx_util.c: utility functions for the VMware ESX driver
4
 *
5
 * Copyright (C) 2010 Red Hat, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
 * Copyright (C) 2009 Maximilian Wilhelm <max@rfc2324.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <config.h>

#include <netdb.h>

#include "internal.h"
#include "datatypes.h"
#include "qparams.h"
#include "util.h"
#include "memory.h"
#include "logging.h"
#include "uuid.h"
36
#include "esx_private.h"
37 38 39 40
#include "esx_util.h"

#define VIR_FROM_THIS VIR_FROM_ESX

41

42

43
int
M
Matthias Bolte 已提交
44
esxUtil_ParseUri(esxUtil_ParsedUri **parsedUri, xmlURIPtr uri)
45
{
M
Matthias Bolte 已提交
46
    int result = -1;
47 48
    struct qparam_set *queryParamSet = NULL;
    struct qparam *queryParam = NULL;
49 50 51
    int i;
    int noVerify;
    int autoAnswer;
M
Matthias Bolte 已提交
52
    char *tmp;
M
Matthias Bolte 已提交
53
    char *saveptr;
54

M
Matthias Bolte 已提交
55
    if (parsedUri == NULL || *parsedUri != NULL) {
56 57
        ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
        return -1;
58 59
    }

M
Matthias Bolte 已提交
60
    if (VIR_ALLOC(*parsedUri) < 0) {
61 62
        virReportOOMError();
        return -1;
63 64
    }

65
#ifdef HAVE_XMLURI_QUERY_RAW
66
    queryParamSet = qparam_query_parse(uri->query_raw);
67
#else
68
    queryParamSet = qparam_query_parse(uri->query);
69 70 71
#endif

    if (queryParamSet == NULL) {
72
        goto cleanup;
73 74 75 76 77
    }

    for (i = 0; i < queryParamSet->n; i++) {
        queryParam = &queryParamSet->p[i];

78
        if (STRCASEEQ(queryParam->name, "transport")) {
M
Matthias Bolte 已提交
79
            VIR_FREE((*parsedUri)->transport);
80

M
Matthias Bolte 已提交
81
            (*parsedUri)->transport = strdup(queryParam->value);
82

M
Matthias Bolte 已提交
83
            if ((*parsedUri)->transport == NULL) {
84
                virReportOOMError();
M
Matthias Bolte 已提交
85
                goto cleanup;
86 87
            }

M
Matthias Bolte 已提交
88 89
            if (STRNEQ((*parsedUri)->transport, "http") &&
                STRNEQ((*parsedUri)->transport, "https")) {
90
                ESX_ERROR(VIR_ERR_INVALID_ARG,
91
                          _("Query parameter 'transport' has unexpected value "
92
                            "'%s' (should be http|https)"),
M
Matthias Bolte 已提交
93
                          (*parsedUri)->transport);
M
Matthias Bolte 已提交
94
                goto cleanup;
95
            }
96
        } else if (STRCASEEQ(queryParam->name, "vcenter")) {
M
Matthias Bolte 已提交
97
            VIR_FREE((*parsedUri)->vCenter);
98

M
Matthias Bolte 已提交
99
            (*parsedUri)->vCenter = strdup(queryParam->value);
100

M
Matthias Bolte 已提交
101
            if ((*parsedUri)->vCenter == NULL) {
102
                virReportOOMError();
M
Matthias Bolte 已提交
103
                goto cleanup;
104
            }
105
        } else if (STRCASEEQ(queryParam->name, "no_verify")) {
106 107
            if (virStrToLong_i(queryParam->value, NULL, 10, &noVerify) < 0 ||
                (noVerify != 0 && noVerify != 1)) {
108
                ESX_ERROR(VIR_ERR_INVALID_ARG,
109 110
                          _("Query parameter 'no_verify' has unexpected value "
                            "'%s' (should be 0 or 1)"), queryParam->value);
M
Matthias Bolte 已提交
111
                goto cleanup;
112
            }
113

M
Matthias Bolte 已提交
114
            (*parsedUri)->noVerify = noVerify != 0;
115 116 117
        } else if (STRCASEEQ(queryParam->name, "auto_answer")) {
            if (virStrToLong_i(queryParam->value, NULL, 10, &autoAnswer) < 0 ||
                (autoAnswer != 0 && autoAnswer != 1)) {
118
                ESX_ERROR(VIR_ERR_INVALID_ARG,
119 120
                          _("Query parameter 'auto_answer' has unexpected "
                            "value '%s' (should be 0 or 1)"), queryParam->value);
M
Matthias Bolte 已提交
121
                goto cleanup;
122
            }
123

M
Matthias Bolte 已提交
124
            (*parsedUri)->autoAnswer = autoAnswer != 0;
M
Matthias Bolte 已提交
125 126
        } else if (STRCASEEQ(queryParam->name, "proxy")) {
            /* Expected format: [<type>://]<hostname>[:<port>] */
M
Matthias Bolte 已提交
127 128 129 130
            (*parsedUri)->proxy = true;
            (*parsedUri)->proxy_type = CURLPROXY_HTTP;
            VIR_FREE((*parsedUri)->proxy_hostname);
            (*parsedUri)->proxy_port = 1080;
M
Matthias Bolte 已提交
131 132

            if ((tmp = STRSKIP(queryParam->value, "http://")) != NULL) {
M
Matthias Bolte 已提交
133
                (*parsedUri)->proxy_type = CURLPROXY_HTTP;
M
Matthias Bolte 已提交
134 135
            } else if ((tmp = STRSKIP(queryParam->value, "socks://")) != NULL ||
                       (tmp = STRSKIP(queryParam->value, "socks5://")) != NULL) {
M
Matthias Bolte 已提交
136
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS5;
M
Matthias Bolte 已提交
137
            } else if ((tmp = STRSKIP(queryParam->value, "socks4://")) != NULL) {
M
Matthias Bolte 已提交
138
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS4;
M
Matthias Bolte 已提交
139
            } else if ((tmp = STRSKIP(queryParam->value, "socks4a://")) != NULL) {
M
Matthias Bolte 已提交
140
                (*parsedUri)->proxy_type = CURLPROXY_SOCKS4A;
M
Matthias Bolte 已提交
141 142 143 144 145 146 147 148 149 150 151 152
            } else if ((tmp = strstr(queryParam->value, "://")) != NULL) {
                *tmp = '\0';

                ESX_ERROR(VIR_ERR_INVALID_ARG,
                          _("Query parameter 'proxy' contains unexpected "
                            "type '%s' (should be (http|socks(|4|4a|5))"),
                          queryParam->value);
                goto cleanup;
            } else {
                tmp = queryParam->value;
            }

M
Matthias Bolte 已提交
153
            (*parsedUri)->proxy_hostname = strdup(tmp);
M
Matthias Bolte 已提交
154

M
Matthias Bolte 已提交
155
            if ((*parsedUri)->proxy_hostname == NULL) {
M
Matthias Bolte 已提交
156 157 158 159
                virReportOOMError();
                goto cleanup;
            }

M
Matthias Bolte 已提交
160 161
            if ((tmp = strchr((*parsedUri)->proxy_hostname, ':')) != NULL) {
                if (tmp == (*parsedUri)->proxy_hostname) {
M
Matthias Bolte 已提交
162 163 164 165 166 167 168 169 170
                    ESX_ERROR(VIR_ERR_INVALID_ARG, "%s",
                              _("Query parameter 'proxy' doesn't contain a "
                                "hostname"));
                    goto cleanup;
                }

                *tmp++ = '\0';

                if (virStrToLong_i(tmp, NULL, 10,
M
Matthias Bolte 已提交
171 172 173
                                   &(*parsedUri)->proxy_port) < 0 ||
                    (*parsedUri)->proxy_port < 1 ||
                    (*parsedUri)->proxy_port > 65535) {
M
Matthias Bolte 已提交
174 175 176 177 178 179
                    ESX_ERROR(VIR_ERR_INVALID_ARG,
                              _("Query parameter 'proxy' has unexpected port"
                                "value '%s' (should be [1..65535])"), tmp);
                    goto cleanup;
                }
            }
180 181 182 183 184 185
        } else {
            VIR_WARN("Ignoring unexpected query parameter '%s'",
                     queryParam->name);
        }
    }

M
Matthias Bolte 已提交
186 187 188
    /* Expected format: [/]<datacenter>/<computeresource>[/<hostsystem>] */
    if (uri->path != NULL) {
        tmp = strdup(uri->path);
189

M
Matthias Bolte 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
        if (tmp == NULL) {
            virReportOOMError();
            goto cleanup;
        }

        if (esxVI_String_DeepCopyValue(&(*parsedUri)->path_datacenter,
                                       strtok_r(tmp, "/", &saveptr)) < 0 ||
            esxVI_String_DeepCopyValue(&(*parsedUri)->path_computeResource,
                                       strtok_r(NULL, "/", &saveptr)) < 0 ||
            esxVI_String_DeepCopyValue(&(*parsedUri)->path_hostSystem,
                                       strtok_r(NULL, "", &saveptr)) < 0) {
            VIR_FREE(tmp);
            goto cleanup;
        }

        VIR_FREE(tmp);
    }

    if ((*parsedUri)->transport == NULL) {
        (*parsedUri)->transport = strdup("https");

        if ((*parsedUri)->transport == NULL) {
212
            virReportOOMError();
M
Matthias Bolte 已提交
213
            goto cleanup;
214 215 216
        }
    }

M
Matthias Bolte 已提交
217
    result = 0;
218

M
Matthias Bolte 已提交
219 220
  cleanup:
    if (result < 0) {
M
Matthias Bolte 已提交
221
        esxUtil_FreeParsedUri(parsedUri);
222 223
    }

M
Matthias Bolte 已提交
224 225
    if (queryParamSet != NULL) {
        free_qparam_set(queryParamSet);
226 227
    }

M
Matthias Bolte 已提交
228
    return result;
229 230 231 232
}



233 234

void
M
Matthias Bolte 已提交
235
esxUtil_FreeParsedUri(esxUtil_ParsedUri **parsedUri)
236
{
M
Matthias Bolte 已提交
237
    if (parsedUri == NULL || *parsedUri == NULL) {
238 239 240
        return;
    }

M
Matthias Bolte 已提交
241 242 243 244 245 246
    VIR_FREE((*parsedUri)->transport);
    VIR_FREE((*parsedUri)->vCenter);
    VIR_FREE((*parsedUri)->proxy_hostname);
    VIR_FREE((*parsedUri)->path_datacenter);
    VIR_FREE((*parsedUri)->path_computeResource);
    VIR_FREE((*parsedUri)->path_hostSystem);
247

M
Matthias Bolte 已提交
248
    VIR_FREE(*parsedUri);
249 250 251 252
}



253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
int
esxUtil_ParseVirtualMachineIDString(const char *id_string, int *id)
{
    /* Try to parse an integer from the complete string. */
    if (virStrToLong_i(id_string, NULL, 10, id) == 0) {
        return 0;
    }

    /*
     * If that fails try to parse an integer from the string tail
     * assuming the naming scheme Virtual Center seems to use.
     */
    if (STRPREFIX(id_string, "vm-")) {
        if (virStrToLong_i(id_string + 3, NULL, 10, id) == 0) {
            return 0;
        }
    }

    return -1;
}



M
Matthias Bolte 已提交
276
int
277
esxUtil_ParseDatastorePath(const char *datastorePath, char **datastoreName,
278
                           char **directoryName, char **directoryAndFileName)
M
Matthias Bolte 已提交
279
{
M
Matthias Bolte 已提交
280
    int result = -1;
281
    char *copyOfDatastorePath = NULL;
282 283 284
    char *tmp = NULL;
    char *saveptr = NULL;
    char *preliminaryDatastoreName = NULL;
285 286
    char *preliminaryDirectoryAndFileName = NULL;
    char *preliminaryFileName = NULL;
M
Matthias Bolte 已提交
287

288 289 290
    if ((datastoreName != NULL && *datastoreName != NULL) ||
        (directoryName != NULL && *directoryName != NULL) ||
        (directoryAndFileName != NULL && *directoryAndFileName != NULL)) {
291
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
292
        return -1;
M
Matthias Bolte 已提交
293 294
    }

295
    if (esxVI_String_DeepCopyValue(&copyOfDatastorePath, datastorePath) < 0) {
M
Matthias Bolte 已提交
296
        goto cleanup;
297 298
    }

299 300 301
    /* Expected format: '[<datastore>] <path>' where <path> is optional */
    if ((tmp = STRSKIP(copyOfDatastorePath, "[")) == NULL || *tmp == ']' ||
        (preliminaryDatastoreName = strtok_r(tmp, "]", &saveptr)) == NULL) {
302
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
303 304
                  _("Datastore path '%s' doesn't have expected format "
                    "'[<datastore>] <path>'"), datastorePath);
M
Matthias Bolte 已提交
305
        goto cleanup;
M
Matthias Bolte 已提交
306 307
    }

308 309
    if (datastoreName != NULL &&
        esxVI_String_DeepCopyValue(datastoreName,
310
                                   preliminaryDatastoreName) < 0) {
M
Matthias Bolte 已提交
311
        goto cleanup;
312 313
    }

314
    preliminaryDirectoryAndFileName = strtok_r(NULL, "", &saveptr);
315

316 317 318 319 320 321
    if (preliminaryDirectoryAndFileName == NULL) {
        preliminaryDirectoryAndFileName = (char *)"";
    } else {
        preliminaryDirectoryAndFileName +=
          strspn(preliminaryDirectoryAndFileName, " ");
    }
M
Matthias Bolte 已提交
322

323 324 325 326 327
    if (directoryAndFileName != NULL &&
        esxVI_String_DeepCopyValue(directoryAndFileName,
                                   preliminaryDirectoryAndFileName) < 0) {
        goto cleanup;
    }
M
Matthias Bolte 已提交
328

329 330 331 332 333 334
    if (directoryName != NULL) {
        /* Split <path> into <directory>/<file> */
        preliminaryFileName = strrchr(preliminaryDirectoryAndFileName, '/');

        if (preliminaryFileName != NULL) {
            *preliminaryFileName++ = '\0';
M
Matthias Bolte 已提交
335 336
        }

337
        if (esxVI_String_DeepCopyValue(directoryName,
338
                                       preliminaryDirectoryAndFileName) < 0) {
M
Matthias Bolte 已提交
339
            goto cleanup;
340
        }
M
Matthias Bolte 已提交
341 342
    }

M
Matthias Bolte 已提交
343 344
    result = 0;

M
Matthias Bolte 已提交
345
  cleanup:
M
Matthias Bolte 已提交
346
    if (result < 0) {
347 348 349 350 351 352 353 354 355 356 357
        if (datastoreName != NULL) {
            VIR_FREE(*datastoreName);
        }

        if (directoryName != NULL) {
            VIR_FREE(*directoryName);
        }

        if (directoryAndFileName != NULL) {
            VIR_FREE(*directoryAndFileName);
        }
M
Matthias Bolte 已提交
358 359
    }

360
    VIR_FREE(copyOfDatastorePath);
M
Matthias Bolte 已提交
361 362 363 364 365 366

    return result;
}



367
int
368
esxUtil_ResolveHostname(const char *hostname,
M
Matthias Bolte 已提交
369
                        char *ipAddress, size_t ipAddress_length)
370 371 372 373 374
{
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    int errcode;

M
Matthias Bolte 已提交
375
    memset(&hints, 0, sizeof (hints));
376 377 378 379 380 381 382 383 384

    hints.ai_flags = AI_ADDRCONFIG;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    errcode = getaddrinfo(hostname, NULL, &hints, &result);

    if (errcode != 0) {
385
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
386
                  _("IP address lookup for host '%s' failed: %s"), hostname,
387 388 389 390 391
                  gai_strerror(errcode));
        return -1;
    }

    if (result == NULL) {
392
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
393
                  _("No IP address for host '%s' found: %s"), hostname,
394 395 396 397
                  gai_strerror(errcode));
        return -1;
    }

M
Matthias Bolte 已提交
398 399
    errcode = getnameinfo(result->ai_addr, result->ai_addrlen, ipAddress,
                          ipAddress_length, NULL, 0, NI_NUMERICHOST);
400 401

    if (errcode != 0) {
402
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
403
                  _("Formating IP address for host '%s' failed: %s"), hostname,
404 405 406 407 408 409 410 411 412 413 414 415 416
                  gai_strerror(errcode));
        freeaddrinfo(result);
        return -1;
    }

    freeaddrinfo(result);

    return 0;
}



int
417
esxUtil_GetConfigString(virConfPtr conf, const char *name, char **string,
418
                        bool optional)
419 420 421 422 423 424 425 426 427 428 429
{
    virConfValuePtr value;

    *string = NULL;
    value = virConfGetValue(conf, name);

    if (value == NULL) {
        if (optional) {
            return 0;
        }

430
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
431
                  _("Missing essential config entry '%s'"), name);
432 433 434 435
        return -1;
    }

    if (value->type != VIR_CONF_STRING) {
436
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
437
                  _("Config entry '%s' must be a string"), name);
438 439 440 441 442 443 444 445
        return -1;
    }

    if (value->str == NULL) {
        if (optional) {
            return 0;
        }

446
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
447
                  _("Missing essential config entry '%s'"), name);
448 449 450 451 452 453
        return -1;
    }

    *string = strdup(value->str);

    if (*string == NULL) {
454
        virReportOOMError();
455 456 457 458 459 460 461 462 463
        return -1;
    }

    return 0;
}



int
464
esxUtil_GetConfigUUID(virConfPtr conf, const char *name, unsigned char *uuid,
465
                      bool optional)
466 467 468 469 470 471 472 473 474
{
    virConfValuePtr value;

    value = virConfGetValue(conf, name);

    if (value == NULL) {
        if (optional) {
            return 0;
        } else {
475
            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
476
                      _("Missing essential config entry '%s'"), name);
477 478 479 480 481
            return -1;
        }
    }

    if (value->type != VIR_CONF_STRING) {
482
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
483
                  _("Config entry '%s' must be a string"), name);
484 485 486 487 488 489 490
        return -1;
    }

    if (value->str == NULL) {
        if (optional) {
            return 0;
        } else {
491
            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
492
                      _("Missing essential config entry '%s'"), name);
493 494 495 496
            return -1;
        }
    }

497
    if (virUUIDParse(value->str, uuid) < 0) {
498
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
499
                  _("Could not parse UUID from string '%s'"), value->str);
500 501
        return -1;
    }
502 503 504 505 506 507 508

    return 0;
}



int
509
esxUtil_GetConfigLong(virConfPtr conf, const char *name, long long *number,
510
                      long long default_, bool optional)
511 512 513 514 515 516 517 518 519 520
{
    virConfValuePtr value;

    *number = default_;
    value = virConfGetValue(conf, name);

    if (value == NULL) {
        if (optional) {
            return 0;
        } else {
521
            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
522
                      _("Missing essential config entry '%s'"), name);
523 524 525 526 527 528 529 530 531
            return -1;
        }
    }

    if (value->type == VIR_CONF_STRING) {
        if (value->str == NULL) {
            if (optional) {
                return 0;
            } else {
532
                ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
533
                          _("Missing essential config entry '%s'"), name);
534 535 536 537 538 539 540
                return -1;
            }
        }

        if (STREQ(value->str, "unlimited")) {
            *number = -1;
        } else if (virStrToLong_ll(value->str, NULL, 10, number) < 0) {
541
            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
542
                      _("Config entry '%s' must represent an integer value"),
543 544 545 546
                      name);
            return -1;
        }
    } else {
547
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
548
                  _("Config entry '%s' must be a string"), name);
549 550 551 552 553 554 555 556 557
        return -1;
    }

    return 0;
}



int
558 559
esxUtil_GetConfigBoolean(virConfPtr conf, const char *name, bool *boolean_,
                         bool default_, bool optional)
560 561 562
{
    virConfValuePtr value;

M
Matthias Bolte 已提交
563
    *boolean_ = default_;
564 565 566 567 568 569
    value = virConfGetValue(conf, name);

    if (value == NULL) {
        if (optional) {
            return 0;
        } else {
570
            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
571
                      _("Missing essential config entry '%s'"), name);
572 573 574 575 576 577 578 579 580
            return -1;
        }
    }

    if (value->type == VIR_CONF_STRING) {
        if (value->str == NULL) {
            if (optional) {
                return 0;
            } else {
581
                ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
582
                          _("Missing essential config entry '%s'"), name);
583 584 585 586 587
                return -1;
            }
        }

        if (STRCASEEQ(value->str, "true")) {
M
Matthias Bolte 已提交
588
            *boolean_ = 1;
589
        } else if (STRCASEEQ(value->str, "false")) {
M
Matthias Bolte 已提交
590
            *boolean_ = 0;
591
        } else {
592
            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
593 594
                      _("Config entry '%s' must represent a boolean value "
                        "(true|false)"), name);
595 596 597
            return -1;
        }
    } else {
598
        ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
599
                  _("Config entry '%s' must be a string"), name);
600 601 602 603 604
        return -1;
    }

    return 0;
}