parallels_network.c 14.5 KB
Newer Older
D
Dmitry Guryanov 已提交
1
/*
2
 * parallels_network.c: core privconn functions for managing
D
Dmitry Guryanov 已提交
3 4
 * Parallels Cloud Server hosts
 *
N
Nehal J Wani 已提交
5
 * Copyright (C) 2013-2014 Red Hat, Inc.
D
Dmitry Guryanov 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * Copyright (C) 2012 Parallels, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include "datatypes.h"
27
#include "dirname.h"
28
#include "viralloc.h"
29
#include "virerror.h"
30
#include "virfile.h"
31
#include "virnetdev.h"
D
Dmitry Guryanov 已提交
32 33
#include "md5.h"
#include "parallels_utils.h"
34
#include "virstring.h"
D
Dmitry Guryanov 已提交
35 36

#define VIR_FROM_THIS VIR_FROM_PARALLELS
37
#define PARALLELS_ROUTED_NETWORK_UUID   "eb593dd1-6846-45b0-84a0-de0729286982"
D
Dmitry Guryanov 已提交
38

39
#define vzParseError()                                                  \
D
Dmitry Guryanov 已提交
40 41 42
    virReportErrorHelper(VIR_FROM_TEST, VIR_ERR_OPERATION_FAILED, __FILE__,    \
                     __FUNCTION__, __LINE__, _("Can't parse prlctl output"))

43
static int vzGetBridgedNetInfo(virNetworkDefPtr def, virJSONValuePtr jobj)
44 45 46 47 48 49 50 51 52 53
{
    const char *ifname;
    char *bridgeLink = NULL;
    char *bridgePath = NULL;
    char *bridgeAddressPath = NULL;
    char *bridgeAddress = NULL;
    int len = 0;
    int ret = -1;

    if (!(ifname = virJSONValueObjectGetString(jobj, "Bound To"))) {
54
        vzParseError();
55 56 57
        goto cleanup;
    }

58
    if (virAsprintf(&bridgeLink, SYSFS_NET_DIR "%s/brport/bridge", ifname) < 0)
59 60 61 62 63 64 65
        goto cleanup;

    if (virFileResolveLink(bridgeLink, &bridgePath) < 0) {
        virReportSystemError(errno, _("cannot read link '%s'"), bridgeLink);
        goto cleanup;
    }

66
    if (VIR_STRDUP(def->bridge, last_component(bridgePath)) < 0)
67 68
        goto cleanup;

69 70
    if (virAsprintf(&bridgeAddressPath, SYSFS_NET_DIR "%s/brport/bridge/address",
                    ifname) < 0)
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
        goto cleanup;

    if ((len = virFileReadAll(bridgeAddressPath, 18, &bridgeAddress)) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Error reading file '%s'"), bridgeAddressPath);

        goto cleanup;
    }

    if (len < VIR_MAC_STRING_BUFLEN) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Error reading MAC from '%s'"), bridgeAddressPath);
    }

    bridgeAddress[VIR_MAC_STRING_BUFLEN - 1] = '\0';
    if (virMacAddrParse(bridgeAddress, &def->mac) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Can't parse MAC '%s'"), bridgeAddress);
        goto cleanup;
    }
    def->mac_specified = 1;

    ret = 0;

95
 cleanup:
96 97 98 99 100 101 102
    VIR_FREE(bridgeLink);
    VIR_FREE(bridgePath);
    VIR_FREE(bridgeAddress);
    VIR_FREE(bridgeAddressPath);
    return ret;
}

103
static int vzGetHostOnlyNetInfo(virNetworkDefPtr def, const char *name)
104 105 106 107 108
{
    const char *tmp;
    virJSONValuePtr jobj = NULL, jobj2;
    int ret = -1;

109
    if (VIR_EXPAND_N(def->ips, def->nips, 1) < 0)
110 111
        goto cleanup;

112
    jobj = vzParseOutput("prlsrvctl", "net", "info", "-j", name, NULL);
113 114

    if (!jobj) {
115
        vzParseError();
116 117 118 119
        goto cleanup;
    }

    if (!(jobj2 = virJSONValueObjectGet(jobj, "Parallels adapter"))) {
120
        vzParseError();
121 122 123
        goto cleanup;
    }

124
    if (VIR_STRDUP(def->ips[0].family, "ipv4") < 0)
125
        goto cleanup;
126

127
    if (!(tmp = virJSONValueObjectGetString(jobj2, "IP address"))) {
128
        vzParseError();
129 130 131 132
        goto cleanup;
    }

    if (virSocketAddrParseIPv4(&def->ips[0].address, tmp) < 0) {
133
        vzParseError();
134 135 136 137
        goto cleanup;
    }

    if (!(tmp = virJSONValueObjectGetString(jobj2, "Subnet mask"))) {
138
        vzParseError();
139 140 141 142
        goto cleanup;
    }

    if (virSocketAddrParseIPv4(&def->ips[0].netmask, tmp) < 0) {
143
        vzParseError();
144 145 146 147
        goto cleanup;
    }

    if (!(jobj2 = virJSONValueObjectGet(jobj, "DHCPv4 server"))) {
148
        vzParseError();
149 150 151
        goto cleanup;
    }

152
    if (VIR_EXPAND_N(def->ips[0].ranges, def->ips[0].nranges, 1) < 0)
153 154 155
        goto cleanup;

    if (!(tmp = virJSONValueObjectGetString(jobj2, "IP scope start address"))) {
156
        vzParseError();
157 158 159 160
        goto cleanup;
    }

    if (virSocketAddrParseIPv4(&def->ips[0].ranges[0].start, tmp) < 0) {
161
        vzParseError();
162 163 164 165
        goto cleanup;
    }

    if (!(tmp = virJSONValueObjectGetString(jobj2, "IP scope end address"))) {
166
        vzParseError();
167 168 169 170
        goto cleanup;
    }

    if (virSocketAddrParseIPv4(&def->ips[0].ranges[0].end, tmp) < 0) {
171
        vzParseError();
172 173 174 175
        goto cleanup;
    }

    ret = 0;
176
 cleanup:
177 178 179 180
    virJSONValueFree(jobj);
    return ret;
}

181
static int
182
vzLoadNetwork(vzConnPtr privconn, virJSONValuePtr jobj)
D
Dmitry Guryanov 已提交
183
{
184 185
    int ret = -1;
    virNetworkObjPtr net = NULL;
D
Dmitry Guryanov 已提交
186 187 188 189 190 191
    virNetworkDefPtr def;
    const char *tmp;
    /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */
    unsigned char md5[MD5_DIGEST_SIZE];

    if (VIR_ALLOC(def) < 0)
192
        goto cleanup;
D
Dmitry Guryanov 已提交
193 194

    if (!(tmp = virJSONValueObjectGetString(jobj, "Network ID"))) {
195
        vzParseError();
D
Dmitry Guryanov 已提交
196 197 198
        goto cleanup;
    }

199 200
    if (VIR_STRDUP(def->name, tmp) < 0)
        goto cleanup;
D
Dmitry Guryanov 已提交
201 202

    /* Network names are unique in Parallels Cloud Server, so we can make
N
Nehal J Wani 已提交
203
     * a UUID from it */
D
Dmitry Guryanov 已提交
204 205 206 207
    md5_buffer(tmp, strlen(tmp), md5);
    memcpy(def->uuid, md5, VIR_UUID_BUFLEN);
    def->uuid_specified = 1;

208
    if (!(tmp = virJSONValueObjectGetString(jobj, "Type"))) {
209
        vzParseError();
210 211 212
        goto cleanup;
    }

213
    if (STREQ(tmp, PARALLELS_BRIDGED_NETWORK_TYPE)) {
214 215
        def->forward.type = VIR_NETWORK_FORWARD_BRIDGE;

216
        if (vzGetBridgedNetInfo(def, jobj) < 0) {
217 218 219 220 221

            /* Only mandatory networks are required to be configured completely */
            if (STRNEQ(def->name, PARALLELS_REQUIRED_BRIDGED_NETWORK))
                ret = 0;

222
            goto cleanup;
223
        }
224
    } else if (STREQ(tmp, PARALLELS_HOSTONLY_NETWORK_TYPE)) {
225 226
        def->forward.type = VIR_NETWORK_FORWARD_NONE;

227
        if (vzGetHostOnlyNetInfo(def, def->name) < 0) {
228 229 230 231 232

            /* Only mandatory networks are required to be configured completely */
            if (STRNEQ(def->name, PARALLELS_REQUIRED_HOSTONLY_NETWORK))
                ret = 0;

233
            goto cleanup;
234
        }
235
    } else {
236
        vzParseError();
237 238 239
        goto cleanup;
    }

240
    if (!(net = virNetworkAssignDef(privconn->networks, def, 0)))
D
Dmitry Guryanov 已提交
241
        goto cleanup;
242
    def = NULL;
D
Dmitry Guryanov 已提交
243 244
    net->active = 1;
    net->autostart = 1;
245
    ret = 0;
D
Dmitry Guryanov 已提交
246

247
 cleanup:
248
    virNetworkObjEndAPI(&net);
D
Dmitry Guryanov 已提交
249
    virNetworkDefFree(def);
250
    return ret;
D
Dmitry Guryanov 已提交
251 252
}

253
static int
254
vzAddRoutedNetwork(vzConnPtr privconn)
255
{
256
    virNetworkObjPtr net = NULL;
257 258 259
    virNetworkDefPtr def;

    if (VIR_ALLOC(def) < 0)
260
        goto cleanup;
261 262 263

    def->forward.type = VIR_NETWORK_FORWARD_ROUTE;

264
    if (VIR_STRDUP(def->name, PARALLELS_DOMAIN_ROUTED_NETWORK_NAME) < 0)
265
        goto cleanup;
266 267 268 269 270 271 272 273

    if (virUUIDParse(PARALLELS_ROUTED_NETWORK_UUID, def->uuid) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Can't parse UUID"));
        goto cleanup;
    }
    def->uuid_specified = 1;

274
    if (!(net = virNetworkAssignDef(privconn->networks, def, 0)))
275
        goto cleanup;
276

277 278
    net->active = 1;
    net->autostart = 1;
279
    virNetworkObjEndAPI(&net);
280

281
    return 0;
282

283
 cleanup:
284
    virNetworkDefFree(def);
285
    return -1;
286 287
}

288
static int vzLoadNetworks(vzConnPtr privconn)
D
Dmitry Guryanov 已提交
289 290 291
{
    virJSONValuePtr jobj, jobj2;
    int ret = -1;
292 293
    int count;
    size_t i;
D
Dmitry Guryanov 已提交
294

295
    jobj = vzParseOutput("prlsrvctl", "net", "list", "-j", NULL);
D
Dmitry Guryanov 已提交
296 297

    if (!jobj) {
298
        vzParseError();
D
Dmitry Guryanov 已提交
299 300 301 302 303
        goto cleanup;
    }

    count = virJSONValueArraySize(jobj);
    if (count < 0) {
304
        vzParseError();
D
Dmitry Guryanov 已提交
305 306 307 308 309 310
        goto cleanup;
    }

    for (i = 0; i < count; i++) {
        jobj2 = virJSONValueArrayGet(jobj, i);
        if (!jobj2) {
311
            vzParseError();
D
Dmitry Guryanov 已提交
312 313 314
            goto cleanup;
        }

315
        if (vzLoadNetwork(privconn, jobj2) < 0)
D
Dmitry Guryanov 已提交
316 317 318
            goto cleanup;
    }

319
    if (vzAddRoutedNetwork(privconn) < 0)
320 321
        goto cleanup;

D
Dmitry Guryanov 已提交
322 323
    ret = 0;

324
 cleanup:
D
Dmitry Guryanov 已提交
325 326 327 328
    virJSONValueFree(jobj);
    return ret;
}

329
virDrvOpenStatus
330
vzNetworkOpen(virConnectPtr conn,
D
Dmitry Guryanov 已提交
331 332
                     unsigned int flags)
{
333
    vzConnPtr privconn = conn->privateData;
334

D
Dmitry Guryanov 已提交
335 336
    virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);

337 338
    if (STRNEQ(conn->driver->name, "vz") &&
        STRNEQ(conn->driver->name, "Parallels"))
D
Dmitry Guryanov 已提交
339 340
        return VIR_DRV_OPEN_DECLINED;

341
    if (!(privconn->networks = virNetworkObjListNew()))
342 343
        goto error;

344
    if (vzLoadNetworks(conn->privateData) < 0)
345
        goto error;
D
Dmitry Guryanov 已提交
346 347

    return VIR_DRV_OPEN_SUCCESS;
348
 error:
349
    virObjectUnref(privconn->networks);
350
    privconn->networks = NULL;
351
    return VIR_DRV_OPEN_ERROR;
D
Dmitry Guryanov 已提交
352 353
}

354
int vzNetworkClose(virConnectPtr conn)
D
Dmitry Guryanov 已提交
355
{
356
    vzConnPtr privconn = conn->privateData;
357 358 359 360

    if (!privconn)
        return 0;

361
    virObjectUnref(privconn->networks);
D
Dmitry Guryanov 已提交
362 363 364
    return 0;
}

365
static int vzConnectNumOfNetworks(virConnectPtr conn)
D
Dmitry Guryanov 已提交
366
{
367
    int nactive;
368
    vzConnPtr privconn = conn->privateData;
D
Dmitry Guryanov 已提交
369

370 371
    nactive = virNetworkObjListNumOfNetworks(privconn->networks,
                                             true, NULL, conn);
D
Dmitry Guryanov 已提交
372 373 374
    return nactive;
}

375
static int vzConnectListNetworks(virConnectPtr conn,
376 377
                                        char **const names,
                                        int nnames)
D
Dmitry Guryanov 已提交
378
{
379
    vzConnPtr privconn = conn->privateData;
380
    int got;
D
Dmitry Guryanov 已提交
381

382 383
    got = virNetworkObjListGetNames(privconn->networks,
                                    true, names, nnames, NULL, conn);
D
Dmitry Guryanov 已提交
384 385 386
    return got;
}

387
static int vzConnectNumOfDefinedNetworks(virConnectPtr conn)
D
Dmitry Guryanov 已提交
388
{
389
    int ninactive;
390
    vzConnPtr privconn = conn->privateData;
D
Dmitry Guryanov 已提交
391

392 393
    ninactive = virNetworkObjListNumOfNetworks(privconn->networks,
                                               false, NULL, conn);
D
Dmitry Guryanov 已提交
394 395 396
    return ninactive;
}

397
static int vzConnectListDefinedNetworks(virConnectPtr conn,
398 399
                                               char **const names,
                                               int nnames)
D
Dmitry Guryanov 已提交
400
{
401
    vzConnPtr privconn = conn->privateData;
402
    int got;
D
Dmitry Guryanov 已提交
403

404 405
    got = virNetworkObjListGetNames(privconn->networks,
                                    false, names, nnames, NULL, conn);
D
Dmitry Guryanov 已提交
406 407 408
    return got;
}

409
static int vzConnectListAllNetworks(virConnectPtr conn,
410 411
                                           virNetworkPtr **nets,
                                           unsigned int flags)
D
Dmitry Guryanov 已提交
412
{
413
    vzConnPtr privconn = conn->privateData;
D
Dmitry Guryanov 已提交
414 415 416

    virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1);

417
    return virNetworkObjListExport(conn, privconn->networks, nets, NULL, flags);
D
Dmitry Guryanov 已提交
418 419
}

420
static virNetworkPtr vzNetworkLookupByUUID(virConnectPtr conn,
D
Dmitry Guryanov 已提交
421 422
                                                  const unsigned char *uuid)
{
423
    vzConnPtr privconn = conn->privateData;
D
Dmitry Guryanov 已提交
424 425 426
    virNetworkObjPtr network;
    virNetworkPtr ret = NULL;

427
    network = virNetworkObjFindByUUID(privconn->networks, uuid);
D
Dmitry Guryanov 已提交
428 429 430 431 432 433 434 435
    if (!network) {
        virReportError(VIR_ERR_NO_NETWORK,
                       "%s", _("no network with matching uuid"));
        goto cleanup;
    }

    ret = virGetNetwork(conn, network->def->name, network->def->uuid);

436
 cleanup:
437
    virNetworkObjEndAPI(&network);
D
Dmitry Guryanov 已提交
438 439 440
    return ret;
}

441
static virNetworkPtr vzNetworkLookupByName(virConnectPtr conn,
D
Dmitry Guryanov 已提交
442 443
                                                  const char *name)
{
444
    vzConnPtr privconn = conn->privateData;
D
Dmitry Guryanov 已提交
445 446 447
    virNetworkObjPtr network;
    virNetworkPtr ret = NULL;

448
    network = virNetworkObjFindByName(privconn->networks, name);
D
Dmitry Guryanov 已提交
449 450 451 452 453 454 455 456
    if (!network) {
        virReportError(VIR_ERR_NO_NETWORK,
                       _("no network with matching name '%s'"), name);
        goto cleanup;
    }

    ret = virGetNetwork(conn, network->def->name, network->def->uuid);

457
 cleanup:
458
    virNetworkObjEndAPI(&network);
D
Dmitry Guryanov 已提交
459 460 461
    return ret;
}

462
static char *vzNetworkGetXMLDesc(virNetworkPtr net,
D
Dmitry Guryanov 已提交
463 464
                                        unsigned int flags)
{
465
    vzConnPtr privconn = net->conn->privateData;
D
Dmitry Guryanov 已提交
466 467 468 469 470
    virNetworkObjPtr network;
    char *ret = NULL;

    virCheckFlags(VIR_NETWORK_XML_INACTIVE, NULL);

471
    network = virNetworkObjFindByUUID(privconn->networks, net->uuid);
D
Dmitry Guryanov 已提交
472 473 474 475 476 477 478 479
    if (!network) {
        virReportError(VIR_ERR_NO_NETWORK,
                       "%s", _("no network with matching uuid"));
        goto cleanup;
    }

    ret = virNetworkDefFormat(network->def, flags);

480
 cleanup:
481
    virNetworkObjEndAPI(&network);
D
Dmitry Guryanov 已提交
482 483 484
    return ret;
}

485
static int vzNetworkIsActive(virNetworkPtr net)
D
Dmitry Guryanov 已提交
486
{
487
    vzConnPtr privconn = net->conn->privateData;
D
Dmitry Guryanov 已提交
488 489 490
    virNetworkObjPtr obj;
    int ret = -1;

491
    obj = virNetworkObjFindByUUID(privconn->networks, net->uuid);
D
Dmitry Guryanov 已提交
492 493 494 495 496 497
    if (!obj) {
        virReportError(VIR_ERR_NO_NETWORK, NULL);
        goto cleanup;
    }
    ret = virNetworkObjIsActive(obj);

498
 cleanup:
499
    virNetworkObjEndAPI(&obj);
D
Dmitry Guryanov 已提交
500 501 502
    return ret;
}

503
static int vzNetworkIsPersistent(virNetworkPtr net)
D
Dmitry Guryanov 已提交
504
{
505
    vzConnPtr privconn = net->conn->privateData;
D
Dmitry Guryanov 已提交
506 507 508
    virNetworkObjPtr obj;
    int ret = -1;

509
    obj = virNetworkObjFindByUUID(privconn->networks, net->uuid);
D
Dmitry Guryanov 已提交
510 511 512 513 514 515
    if (!obj) {
        virReportError(VIR_ERR_NO_NETWORK, NULL);
        goto cleanup;
    }
    ret = obj->persistent;

516
 cleanup:
517
    virNetworkObjEndAPI(&obj);
D
Dmitry Guryanov 已提交
518 519 520
    return ret;
}

521
static int vzNetworkGetAutostart(virNetworkPtr net,
D
Dmitry Guryanov 已提交
522 523
                                 int *autostart)
{
524
    vzConnPtr privconn = net->conn->privateData;
D
Dmitry Guryanov 已提交
525 526 527
    virNetworkObjPtr network;
    int ret = -1;

528
    network = virNetworkObjFindByUUID(privconn->networks, net->uuid);
D
Dmitry Guryanov 已提交
529 530 531 532 533 534 535 536 537
    if (!network) {
        virReportError(VIR_ERR_NO_NETWORK,
                       "%s", _("no network with matching uuid"));
        goto cleanup;
    }

    *autostart = network->autostart;
    ret = 0;

538
 cleanup:
539
    virNetworkObjEndAPI(&network);
D
Dmitry Guryanov 已提交
540 541
    return ret;
}
542

543
virNetworkDriver vzNetworkDriver = {
544
    .name = "Parallels",
545 546 547 548 549 550 551 552 553 554 555
    .connectNumOfNetworks = vzConnectNumOfNetworks, /* 1.0.1 */
    .connectListNetworks = vzConnectListNetworks, /* 1.0.1 */
    .connectNumOfDefinedNetworks = vzConnectNumOfDefinedNetworks, /* 1.0.1 */
    .connectListDefinedNetworks = vzConnectListDefinedNetworks, /* 1.0.1 */
    .connectListAllNetworks = vzConnectListAllNetworks, /* 1.0.1 */
    .networkLookupByUUID = vzNetworkLookupByUUID, /* 1.0.1 */
    .networkLookupByName = vzNetworkLookupByName, /* 1.0.1 */
    .networkGetXMLDesc = vzNetworkGetXMLDesc, /* 1.0.1 */
    .networkGetAutostart = vzNetworkGetAutostart, /* 1.0.1 */
    .networkIsActive = vzNetworkIsActive, /* 1.0.1 */
    .networkIsPersistent = vzNetworkIsPersistent, /* 1.0.1 */
D
Dmitry Guryanov 已提交
556
};