[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libvir] Initial OpenVZ Support Patches



Hi,
Here are the OpenVZ support patches. The .c and .h files go into src/
This is being released in the spirit of releasing early. Only the basic
stuff is done and development is continuing.

What works:

* Getting number of active/inactive domains
* Listing active/inactive domains
* Creating a domain that has been already defined (using OpenVZ tools)
* Shutdown, start, reboot
* configure.in has a new switch --with-openvz(on by default), use
--without-openvz to switch it off.

After I get feedback, I will work on it and add other features later on.
There is no need for any OpenVZ tools code. Just the OpenVZ kernel and
tools need to be installed. This code depends on the OpenVZ tool
binaries.

Differences from QEMU/Xen:

* ID and name are same
* Not possible to create temporary domains and do away with them.
Creating a domain will involve untarring a template cache and bringing
it up.
* No readonly access. OpenVZ tools need root access.

There are other design issues to be discussed. These issues spring from
the fact that OpenVZ is very different from Xen/QEMU. I will discuss
these issues in further mails.

Here is a sample session transcript:

shuveb debian-openvz:~/libvirt$ sudo src/virsh -c openvz:///system
Welcome to lt-virsh, the virtualization interactive terminal.

Type:  'help' for help with commands
       'quit' to quit

virsh # list
 Id Name                 State
----------------------------------
101 101                  running
102 102                  running
103 103                  running

virsh # shutdown 101
Domain 101 is being shutdown

virsh # list
 Id Name                 State
----------------------------------
102 102                  running
103 103                  running

virsh # list --all
 Id Name                 State
----------------------------------
102 102                  running
103 103                  running
  - 101                  shut off

virsh # start 101
Domain 101 started

virsh # list 
 Id Name                 State
----------------------------------
101 101                  running
102 102                  running
103 103                  running

virsh #

Thanks and Regards,

-- 
Shuveb Hussain

Unix is very user friendly. It is just a 
little choosy about who its friends are
http://www.binarykarma.com
Index: configure.in
===================================================================
RCS file: /data/cvs/libvirt/configure.in,v
retrieving revision 1.76
diff -a -u -r1.76 configure.in
--- configure.in	12 Jul 2007 08:36:48 -0000	1.76
+++ configure.in	16 Jul 2007 07:25:28 -0000
@@ -75,6 +75,8 @@
 [  --with-xen              add XEN support (on)])
 AC_ARG_WITH(qemu,
 [  --with-qemu             add QEMU/KVM support (on)])
+AC_ARG_WITH(openvz,
+[  --with-openvz           add OpenVZ support (on)])
 AC_ARG_WITH(test,
 [  --with-test             add test driver support (on)])
 AC_ARG_WITH(remote,
@@ -173,6 +175,11 @@
 LIBVIRT_FEATURES=
 WITH_XEN=0
 
+if test "$with_openvz" = "no" ; then
+    echo "Disabling OpenVZ support"
+else
+    LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_OPENVZ"
+fi
 if test "$with_qemu" = "no" ; then
     echo "Disabling QEMU/KVM support"
 else
Index: src/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/src/Makefile.am,v
retrieving revision 1.44
diff -a -u -r1.44 Makefile.am
--- src/Makefile.am	27 Jun 2007 00:12:29 -0000	1.44
+++ src/Makefile.am	16 Jul 2007 07:25:28 -0000
@@ -47,7 +47,9 @@
 		iptables.c iptables.h 				\
 		uuid.c uuid.h 					\
 		qemu_driver.c qemu_driver.h 			\
-		qemu_conf.c qemu_conf.h
+		qemu_conf.c qemu_conf.h					\
+		openvz_conf.c openvz_conf.h				\
+		openvz_driver.c openvz_driver.h 			
 
 SERVER_SOURCES = 						\
 		../qemud/protocol.h ../qemud/protocol.c		\
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.30
diff -a -u -r1.30 driver.h
--- src/driver.h	26 Jun 2007 22:56:14 -0000	1.30
+++ src/driver.h	16 Jul 2007 07:25:28 -0000
@@ -21,6 +21,7 @@
     VIR_DRV_TEST = 2,
     VIR_DRV_QEMU = 3,
     VIR_DRV_REMOTE = 4,
+    VIR_DRV_OPENVZ = 5,
 } virDrvNo;
 
 
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.88
diff -a -u -r1.88 libvirt.c
--- src/libvirt.c	12 Jul 2007 08:34:51 -0000	1.88
+++ src/libvirt.c	16 Jul 2007 07:25:30 -0000
@@ -85,12 +85,15 @@
 #ifdef WITH_TEST
     if (testRegister() == -1) return -1;
 #endif
-#ifdef WITH_QEMU
+#ifdef WITH_QEMU 
     if (qemudRegister() == -1) return -1;
 #endif
 #ifdef WITH_XEN
     if (xenUnifiedRegister () == -1) return -1;
 #endif
+#ifdef WITH_OPENVZ
+    if (openvzRegister() == -1) return -1;
+#endif
 #ifdef WITH_REMOTE
     if (remoteRegister () == -1) return -1;
 #endif

/*
 * openvz_conf.c: config functions for managing OpenVZ VEs
 *
 * Copyright (C) 2006, 2007 Binary Karma
 * Copyright (C) 2006 Shuveb Hussain
 *
 * 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
 *
 * Author: Shuveb Hussain <shuveb binarykarma com>
 */

#ifdef WITH_OPENVZ

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <strings.h>
#include <time.h>

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/uri.h>

#include <libvirt/virterror.h>

#include "openvz_conf.h"
#include "openvz_driver.h"
#include "uuid.h"
#include "buf.h"


/* For errors internal to this library. */
static void
error (virConnectPtr conn, virErrorNumber code, const char *info)
{
    const char *errmsg;

    errmsg = __virErrorMsg (code, info);
    __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE,
                     code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0,
                     errmsg, info);
}

struct openvz_vm *openvzFindVMByID(const struct openvz_driver *driver, int id) {
    struct openvz_vm *vm = driver->vms;

    while (vm) {
        if (vm->vpsid == id)
            return vm;
        vm = vm->next;
    }

    return NULL;
}

struct openvz_vm *openvzFindVMByUUID(const struct openvz_driver *driver,
                                   const unsigned char *uuid) {
    struct openvz_vm *vm = driver->vms;

    while (vm) {
        if (!memcmp(vm->vmdef->uuid, uuid, OPENVZ_UUID_MAX))
            return vm;
        vm = vm->next;
    }

    return NULL;
}

struct openvz_vm *openvzFindVMByName(const struct openvz_driver *driver,
                                   const char *name) {
    struct  openvz_vm *vm = driver->vms;

    while (vm) {
        if (!strcmp(vm->vmdef->name, name))
            return vm;
        vm = vm->next;
    }

    return NULL;
}

/* Free all memory associated with a struct openvz_vm object */
void openvzFreeVMDef(struct openvz_vm_def *def) {
    struct ovz_quota *quota = def->fs.quota;
    struct ovz_ip *ip = def->net.ips;
    struct ovz_ns *ns = def->net.ns;

    while (quota) {
        struct ovz_quota *prev = quota;
        quota = quota->next;
        free(prev);
    }
    while (ip) {
        struct ovz_ip *prev = ip;
        ip = ip->next;
        free(prev);
    }
    while (ns) {
        struct ovz_ns *prev = ns;
        ns = ns->next;
        free(prev);
    }

    free(def);
}

/*
 * Parses a libvirt XML definition of a guest, and populates the
 * the openvz_vm struct with matching data about the guests config
 */
static struct openvz_vm_def *openvzParseXML(virConnectPtr conn,
                                                xmlDocPtr xml) {
    xmlNodePtr root = NULL;
    xmlChar *prop = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
    struct openvz_vm_def *def;

    if (!(def = calloc(1, sizeof(struct openvz_vm_def)))) {
        error(conn, VIR_ERR_NO_MEMORY, "xmlXPathContext");
        return NULL;
    }

    /* Prepare parser / xpath context */
    root = xmlDocGetRootElement(xml);
    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "incorrect root element");
        goto bail_out;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
        error(conn, VIR_ERR_NO_MEMORY, "xmlXPathContext");
        goto bail_out;
    }


    /* Find out what type of QEMU virtualization to use */
    if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "missing domain type attribute");
        goto bail_out;
    }

    if (strcmp((char *)prop, "openvz")){
        error(conn, VIR_ERR_INTERNAL_ERROR, "invalid domain type attribute");
        goto bail_out;
    }
    free(prop);
    prop = NULL;

    /* Extract domain name */
    obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
        error(conn, VIR_ERR_INTERNAL_ERROR,"invalid domain name");
        goto bail_out;
    }
    if (0/* check if VPS ID is < 101 */) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "VPS ID is less than 101");
        goto bail_out;
    }
    strcpy(def->name, (const char *)obj->stringval);
    xmlXPathFreeObject(obj);

    /* Extract domain uuid */
    obj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
        int err;
        if ((err = virUUIDGenerate(def->uuid))) {
            error(conn, VIR_ERR_INTERNAL_ERROR,
                             "Failed to generate UUID");
            goto bail_out;
        }
    } else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "malformed uuid element");
        goto bail_out;
    }
    xmlXPathFreeObject(obj);

    /* Extract filesystem info */
    obj = xmlXPathEval(BAD_CAST "string(/domain/filesystem/template[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
        error(conn, VIR_ERR_OS_TYPE, NULL);
        goto bail_out;
    }
    strcpy(def->fs.tmpl, (const char *)obj->stringval);
    xmlXPathFreeObject(obj);

    /* TODO Add quota processing here */

    /* TODO analysis of the network devices */

    xmlXPathFreeContext(ctxt);

    return def;

 bail_out:
    if (prop)
        free(prop);
    if (obj)
        xmlXPathFreeObject(obj);
    if (ctxt)
        xmlXPathFreeContext(ctxt);
    openvzFreeVMDef(def);
    return NULL;
}

struct openvz_vm_def *
openvzParseVMDef(virConnectPtr conn,
                const char *xmlStr,
                const char *displayName) {
    xmlDocPtr xml;
    struct openvz_vm_def *def = NULL;

    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "domain.xml", NULL,
                           XML_PARSE_NOENT | XML_PARSE_NONET |
                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
        error(conn, VIR_ERR_XML_ERROR, NULL);
        return NULL;
    }

    def = openvzParseXML(conn, xml);

    xmlFreeDoc(xml);

    return def;
}

struct openvz_vm *
openvzGetVPSInfo(virConnectPtr conn) {
    FILE *fp;
    int veid, ret;
    char status[16];
    char uuidstr[(VIR_UUID_BUFLEN * 2) + 1];
    struct openvz_vm *vm;
    struct openvz_vm  **pnext;
    struct openvz_driver *driver;
    struct openvz_vm_def *vmdef;

    vm =  NULL;
    driver = conn->privateData;
    driver->num_active = 0;
    driver->num_inactive = 0;
    
    if((fp = popen(VZLIST " -a -ovpsid,status -H 2>/dev/null", "r")) == NULL) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "popen failed");
        return NULL;
    }
    pnext = &vm; 
    while(!feof(fp)) { 
        *pnext = malloc(sizeof(struct openvz_vm));
        if(!*pnext) {
            error(conn, VIR_ERR_INTERNAL_ERROR, "malloc failed");
            return NULL;
        }
        
        if(!vm)
            vm = *pnext;

        fscanf(fp, "%d %s\n", &veid, status);
        if(strcmp(status, "stopped")) { 
            (*pnext)->status = VIR_DOMAIN_RUNNING;
            driver->num_active ++;
            (*pnext)->vpsid = veid;
        }
        else {
            (*pnext)->status = VIR_DOMAIN_SHUTOFF;
            driver->num_inactive ++;
            (*pnext)->vpsid = -1;    /* inactive domains don't have their ID set in libvirt,
                                        thought this doesn't make sense for OpenVZ */
        }

        vmdef = malloc(sizeof(struct openvz_vm_def));
        if(!vmdef) {
            error(conn, VIR_ERR_INTERNAL_ERROR, "malloc failed");
            return NULL;
        }
        
        snprintf(vmdef->name, OPENVZ_NAME_MAX,  "%i", veid);
        openvzGetVPSUUID(veid, uuidstr);
        ret = sscanf(uuidstr, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                    (unsigned int *)&vmdef->uuid[0], (unsigned int *)&vmdef->uuid[1], (unsigned int *)&vmdef->uuid[2], 
                    (unsigned int *)&vmdef->uuid[3], (unsigned int *)&vmdef->uuid[4], (unsigned int *)&vmdef->uuid[5], 
                    (unsigned int *)&vmdef->uuid[6], (unsigned int *)&vmdef->uuid[7], (unsigned int *)&vmdef->uuid[8], 
                    (unsigned int *)&vmdef->uuid[9], (unsigned int *)&vmdef->uuid[10], (unsigned int *)&vmdef->uuid[11], 
                    (unsigned int *)&vmdef->uuid[12], (unsigned int *)&vmdef->uuid[13], (unsigned int *)&vmdef->uuid[14],
                    (unsigned int *)&vmdef->uuid[15]);

        if(ret != 16) {
            error(conn, VIR_ERR_INTERNAL_ERROR, "UUID in config file malformed");
            return NULL;
        }

        (*pnext)->vmdef = vmdef;
        pnext = &(*pnext)->next;
    }
    return vm;
}

char *openvzLocateConfDir(void)
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

    while(conf_dir_list[i]) {
        if(!access(conf_dir_list[i], F_OK))
                return strdup(conf_dir_list[i]);
        i ++;
    }

    return NULL;
}

/* Richard Steven's classic readline() function */

int readline(int fd, char *ptr, int maxlen)
{
    int n, rc;
    char c;

    for(n = 1; n < maxlen; n ++) {
        if( (rc = read(fd, &c, 1)) == 1) {
            *ptr++ = c;
            if(c == '\n')
                break;
        }
        else if(rc == 0) {
            if(n == 1)
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

void openvzGenerateUUID(unsigned char *uuid)
{
    unsigned int i;
    int fd;
    
    /* seed rand() with kernel entrophy */
    fd =  open("/dev/urandom", O_RDONLY);
    if(fd != -1) {
        read(fd, &i, sizeof(int));
        srand(i);
        close(fd);
    }
    else {
        srand((int) time(NULL));
    }

    for (i = 0 ; i < VIR_UUID_BUFLEN ; i++) {
        uuid[i] = (unsigned char)(1 + (int) (256.0 * (rand() / (RAND_MAX + 1.0))));
    }
}

int openvzGetVPSUUID(int vpsid, char *uuidbuf)
{
    char conf_file[PATH_MAX];
    char line[1024];
    char uuid[1024];
    char iden[1024];
    char *conf_dir;
    int fd, ret;

    conf_dir = openvzLocateConfDir();
    sprintf(conf_file, "%s/%d.conf", conf_dir, vpsid);
    free(conf_dir);

    fd = open(conf_file, O_RDWR);
    if(fd == -1)
        return -1;

    while(1) {
        ret = readline(fd, line, sizeof(line));
        if(ret == -1)
            return -1;

        if(ret == 0) { /* EoF, UUID was not found */
            uuidbuf[0] = (char)NULL;
            break;
        }

        sscanf(line, "%s %s\n", iden, uuid);
        if(!strcmp(iden, "#UUID:")) {
            strncpy(uuidbuf, uuid, (VIR_UUID_BUFLEN * 2) +1);
            break;
        }
    }
    return 0;
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */

int openvzSetUUID(int vpsid)
{
    char conf_file[PATH_MAX];
    char uuid[(VIR_UUID_BUFLEN * 2) + 1];
    unsigned char new_uuid[VIR_UUID_BUFLEN];
    char *conf_dir;
    int fd, ret, i;

    conf_dir = openvzLocateConfDir();
    sprintf(conf_file, "%s/%d.conf", conf_dir, vpsid);
    free(conf_dir);

    fd = open(conf_file, O_RDWR);
    if(fd == -1)
        return -1;

    ret = openvzGetVPSUUID(vpsid, uuid);
    if(ret == -1)
        return -1;

    if(uuid[0] == (int)NULL) {
        openvzGenerateUUID(new_uuid);
        bzero(uuid, (VIR_UUID_BUFLEN * 2) + 1);
        for(i = 0; i < VIR_UUID_BUFLEN; i ++)
            sprintf(uuid + (i * 2), "%02x", (unsigned char)new_uuid[i]);
    
        lseek(fd, 0, SEEK_END);
        write(fd, "\n#UUID: ", 8);
        write(fd, uuid, strlen(uuid));
        write(fd, "\n", 1);
        close(fd);
    }

    return 0;
}

/*
 * Scan VPS config files and see if they have a UUID.
 * If not, assign one. Just append one to the config
 * file as comment so that the OpenVZ tools ignore it.
 *
 */

int openvzAssignUUIDs(void)
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
    int vpsid, res;
    char ext[8];

    conf_dir = openvzLocateConfDir();

    dp = opendir(conf_dir);
    if(dp == NULL) {
        free(conf_dir);
        return 0;
    }

    while((dent = readdir(dp))) {
        res = sscanf(dent->d_name, "%d.%5s", &vpsid, ext);
        if(!(res == 2 && !strcmp(ext, "conf")))
            continue;
        if(vpsid > 0) /* '0.conf' belongs to the host, ignore it */
            openvzSetUUID(vpsid);
    }
    closedir(dp);
    free(conf_dir);
    return 0;
}

#endif

/*
 * openvz_config.h: config information for OpenVZ VPSs
 *
 * Copyright (C) 2006, 2007 Binary Karma.
 * Copyright (C) 2006 Shuveb Hussain
 *
 * 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
 *
 * Author: Shuveb Hussain <shuveb binarykarma com>
 */

#ifndef OPENVZ_CONF_H
#define OPENVZ_CONF_H

#include "openvz_driver.h"

#define OPENVZ_NAME_MAX 8
#define OPENVZ_UUID_MAX 16
#define OPENVZ_TMPL_MAX 256
#define OPENVZ_UNAME_MAX    32
#define OPENVZ_IP_MAX   16
#define OPENVZ_HOSTNAME_MAX 256
#define OPENVZ_PROFILE_MAX  256

enum openvz_quota{
    VM_LEVEL = 0,
    USER_LEVEL = 1,
};

/* TODO Add more properties here */
struct vps_props {
   int kmemsize;    /* currently held */
   int kmemsize_m;  /* max held */
   int kmemsize_b;  /* barrier */
   int kmemsize_l;  /* limit */
   int kmemsize_f;  /* fail count */

};

struct openvz_fs_def {
    char tmpl[OPENVZ_TMPL_MAX];
    struct ovz_quota *quota;
};

struct ovz_ip {
    char ip[OPENVZ_IP_MAX];
    char netmask[OPENVZ_IP_MAX];
    struct ovz_ip *next;
};

struct ovz_ns {
    char ip[OPENVZ_IP_MAX];
    struct ovz_ns *next;
};

struct openvz_net_def {
    char hostname[OPENVZ_HOSTNAME_MAX];
    char def_gw[OPENVZ_IP_MAX];
    struct ovz_ip *ips;
    struct ovz_ns *ns;
};

struct openvz_vm_def {
    char name[OPENVZ_NAME_MAX];
    unsigned char uuid[OPENVZ_UUID_MAX];
    char profile[OPENVZ_PROFILE_MAX];
    struct openvz_fs_def fs;
    struct openvz_net_def net;
};

struct ovz_quota {
    enum openvz_quota type;
    unsigned int size;
    char uname[OPENVZ_UNAME_MAX];
    struct ovz_quota *next;
};

struct openvz_vm {
    int vpsid;
    int status;
    struct openvz_vm_def *vmdef;
    struct openvz_vm *next;
};

char *openvzLocateConfDir(void);
int readline(int fd, char *ptr, int maxlen);
static void error (virConnectPtr conn, virErrorNumber code, const char *info);
struct openvz_vm *openvzFindVMByID(const struct openvz_driver *driver, int id); 
struct openvz_vm *openvzFindVMByUUID(const struct openvz_driver *driver, 
                                            const unsigned char *uuid);
struct openvz_vm *openvzFindVMByName(const struct openvz_driver *driver,
                                   const char *name);
void openvzFreeVMDef(struct openvz_vm_def *def);
static struct openvz_vm_def *openvzParseXML(virConnectPtr conn, xmlDocPtr xml);
struct openvz_vm_def *openvzParseVMDef(virConnectPtr conn, const char *xmlStr,
                                            const char *displayName);
struct openvz_vm *openvzGetVPSInfo(virConnectPtr conn);
void openvzGenerateUUID(unsigned char *uuid);
int openvzGetVPSUUID(int vpsid, char *uuidbuf);
int openvzSetUUID(int vpsid);
int openvzAssignUUIDs(void);
#endif /* OPENVZ_CONF_H */
/*
 * openvz_driver.c: core driver methods for managing OpenVZ VEs
 *
 * Copyright (C) 2006, 2007 Binary Karma
 * Copyright (C) 2006 Shuveb Hussain
 *
 * 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
 *
 * Author: Shuveb Hussain <shuveb binarykarma com>
 */

#ifdef WITH_OPENVZ

#include <config.h>

#define _GNU_SOURCE /* for asprintf */

#include <sys/types.h>
#include <sys/poll.h>
#include <dirent.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <strings.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <paths.h>
#include <ctype.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/wait.h>

#include <libvirt/virterror.h>

#include "event.h"
#include "buf.h"
#include "openvz_driver.h"
#include "openvz_conf.h"


#define openvzLog(level, msg...) fprintf(stderr, msg)

static virDomainPtr openvzDomainLookupByID(virConnectPtr conn, int id);
static char *openvzGetOSType(virDomainPtr dom);
static virDomainPtr openvzDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid);
static virDomainPtr openvzDomainLookupByName(virConnectPtr conn, const char *name);
static int openvzDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info);
static int openvzDomainShutdown(virDomainPtr dom);
static int openvzDomainReboot(virDomainPtr dom, unsigned int flags);
static int openvzDomainCreate(virDomainPtr dom);
static virDrvOpenStatus openvzOpen(virConnectPtr conn, const char *name,
                           int flags ATTRIBUTE_UNUSED);
static int openvzClose(virConnectPtr conn);
static const char *openvzGetType(virConnectPtr conn ATTRIBUTE_UNUSED);
static int openvzListDomains(virConnectPtr conn, int *ids, int nids);
static int openvzNumDomains(virConnectPtr conn);
static int openvzListDefinedDomains(virConnectPtr conn, char **const names, int nnames);
static int openvzNumDefinedDomains(virConnectPtr conn);
static int openvzStartup(void);
static int openvzShutdown(void);
static int openvzReload(void);
static int openvzActive(void);
static int openvzCloseNetwork(virConnectPtr conn);
static virDrvOpenStatus openvzOpenNetwork(virConnectPtr conn, const char *name ATTRIBUTE_UNUSED,
                                         int flags ATTRIBUTE_UNUSED);
struct openvz_driver ovz_driver;

/* For errors internal to this library. */
static void
error (virConnectPtr conn, virErrorNumber code, const char *info)
{
    const char *errmsg;

    errmsg = __virErrorMsg (code, info);
    __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE,
                     code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0,
                     errmsg, info);
}

static virDomainPtr openvzDomainLookupByID(virConnectPtr conn,
                                   int id) {
    struct openvz_driver *driver = (struct openvz_driver *)conn->privateData;
    struct openvz_vm *vm = openvzFindVMByID(driver, id);
    virDomainPtr dom;

    if (!vm) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "no domain with matching id");
        return NULL;
    }

    dom = virGetDomain(conn, vm->vmdef->name, vm->vmdef->uuid);
    if (!dom) {
        error(conn, VIR_ERR_NO_MEMORY, "virDomainPtr");
        return NULL;
    }

    dom->id = vm->vpsid;
    return dom;
}

static char *openvzGetOSType(virDomainPtr dom)
{
    /* OpenVZ runs on Linux and runs only Linux */
    return strdup("Linux");
}


static virDomainPtr openvzDomainLookupByUUID(virConnectPtr conn,
                                     const unsigned char *uuid) {
    struct  openvz_driver *driver = (struct openvz_driver *)conn->privateData;
    struct openvz_vm *vm = openvzFindVMByUUID(driver, uuid);
    virDomainPtr dom;

    if (!vm) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "no domain with matching uuid");
        return NULL;
    }

    dom = virGetDomain(conn, vm->vmdef->name, vm->vmdef->uuid);
    if (!dom) {
        error(conn, VIR_ERR_NO_MEMORY, "virDomainPtr");
        return NULL;
    }

    dom->id = vm->vpsid;
    return dom;
}

static virDomainPtr openvzDomainLookupByName(virConnectPtr conn,
                                     const char *name) {
    struct openvz_driver *driver = (struct openvz_driver *)conn->privateData;
    struct openvz_vm *vm = openvzFindVMByName(driver, name);
    virDomainPtr dom;

    if (!vm) {
        error(conn, VIR_ERR_INTERNAL_ERROR, "no domain with matching name");
        return NULL;
    }

    dom = virGetDomain(conn, vm->vmdef->name, vm->vmdef->uuid);
    if (!dom) {
        error(conn, VIR_ERR_NO_MEMORY, "virDomainPtr");
        return NULL;
    }

    dom->id = vm->vpsid;
    return dom;
}

static int openvzDomainGetInfo(virDomainPtr dom,
                       virDomainInfoPtr info) {
    struct openvz_driver *driver = (struct openvz_driver *)dom->conn->privateData;
    struct openvz_vm *vm = openvzFindVMByUUID(driver, dom->uuid);
    if (!vm) {
        error(dom->conn, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
        return -1;
    }

    info->state = vm->status;

    /* TODO These need to be calculated differently for OpenVZ */
    //info->cpuTime = 
    //info->maxMem = vm->def->maxmem;
    //info->memory = vm->def->memory;
    //info->nrVirtCpu = vm->def->vcpus;
    return 0;
}

static int openvzDomainShutdown(virDomainPtr dom) {
    char cmdbuf[1024];
    int ret;
    struct openvz_driver *driver = (struct openvz_driver *)dom->conn->privateData;
    struct openvz_vm *vm = openvzFindVMByID(driver, dom->id);

    if (!vm) {
        error(dom->conn, VIR_ERR_INVALID_DOMAIN, "no domain with matching id");
        return -1;
    }
    
    if (vm->status != VIR_DOMAIN_RUNNING) {
        error(dom->conn, VIR_ERR_OPERATION_DENIED, "domain is not in running state");
        return -1;
    }

    snprintf(cmdbuf, 1024, VZCTL " stop %d >/dev/null 2>&1", dom->id);
    ret = system(cmdbuf);
    if(WEXITSTATUS(ret)) {
        error(dom->conn, VIR_ERR_OPERATION_FAILED, "could not shutdown domain");
        return -1;
    }
    vm->vpsid = -1;
    vm->status = VIR_DOMAIN_SHUTOFF;
    ovz_driver.num_inactive ++;
    ovz_driver.num_active --;
    
    return ret;
}

static int openvzDomainReboot(virDomainPtr dom, unsigned int flags) {
    char cmdbuf[1024];
    int ret;
    struct openvz_driver *driver = (struct openvz_driver *)dom->conn->privateData;
    struct openvz_vm *vm = openvzFindVMByID(driver, dom->id);

    if (!vm) {
        error(dom->conn, VIR_ERR_INVALID_DOMAIN, "no domain with matching id");
        return -1;
    }
    
    if (vm->status != VIR_DOMAIN_RUNNING) {
        error(dom->conn, VIR_ERR_OPERATION_DENIED, "domain is not in running state");
        return -1;
    }

    snprintf(cmdbuf, 1024, VZCTL " restart %d >/dev/null 2>&1", dom->id);
    ret = system(cmdbuf);
    if(WEXITSTATUS(ret)) {
        error(dom->conn, VIR_ERR_OPERATION_FAILED, "could not reboot domain");
        return -1;
    }
    
    return ret;
}

static int openvzDomainCreate(virDomainPtr dom) {
    char cmdbuf[1024];
    int ret;
    struct openvz_driver *driver = (struct openvz_driver *)dom->conn->privateData;
    struct openvz_vm *vm = openvzFindVMByID(driver, dom->id);
    struct openvz_vm_def *vmdef;

    if (!vm) {
        error(dom->conn, VIR_ERR_INVALID_DOMAIN, "no domain with matching id");
        return -1;
    }
    
    if (vm->status != VIR_DOMAIN_SHUTOFF) {
        error(dom->conn, VIR_ERR_OPERATION_DENIED, "domain is not in shutoff state");
        return -1;
    }

    vmdef = vm->vmdef;
    snprintf(cmdbuf, 1024, VZCTL " start %s >/dev/null 2>&1", vmdef->name);
    ret = system(cmdbuf);
    if(WEXITSTATUS(ret)) {
        error(dom->conn, VIR_ERR_OPERATION_FAILED, "could not start domain");
        return -1;
    }
    sscanf(vmdef->name, "%d", &vm->vpsid); 
    vm->status = VIR_DOMAIN_RUNNING;
    ovz_driver.num_inactive --;
    ovz_driver.num_active ++;

    return ret;
}

static virDrvOpenStatus openvzOpen(virConnectPtr conn,
                           const char *name,
                           int flags ATTRIBUTE_UNUSED) {
    struct openvz_vm *vms;

    /* Just check if the guy is root. Nothing really to open for OpenVZ */
    if (getuid()) { // OpenVZ tools can only be used by r00t
            return VIR_DRV_OPEN_DECLINED;
    } else {
        if (strcmp(name, "openvz:///system")) 
            return VIR_DRV_OPEN_DECLINED;
    }

    conn->privateData = &ovz_driver;

    virStateInitialize();
    vms = openvzGetVPSInfo(conn);
    ovz_driver.vms = vms;

    return VIR_DRV_OPEN_SUCCESS;
}

static int openvzClose(virConnectPtr conn) {
    
    struct openvz_driver *driver = (struct openvz_driver *)conn->privateData;
    struct openvz_vm *vm = driver->vms;
    

    while(vm) {
        openvzFreeVMDef(vm->vmdef);
        vm = vm->next;
    }
    vm = driver->vms;
    while (vm) {
        struct openvz_vm *prev = vm;
        vm = vm->next;
        free(prev);
    }
    
    conn->privateData = NULL;

    return 0;
}

static const char *openvzGetType(virConnectPtr conn ATTRIBUTE_UNUSED) {
    return strdup("OpenVZ");
}


static int openvzListDomains(virConnectPtr conn, int *ids, int nids) {
    int got = 0;
    int veid;
    FILE *fp;

    if((fp = popen(VZLIST " -o vpsid -H 2> /dev/null", "r")) == NULL){
        error(conn, VIR_ERR_INTERNAL_ERROR, "Could not popen " VZLIST);
        return (int)NULL;
    }

    while(!(feof(fp)) && got < nids){
        fscanf(fp, "%d\n", &veid);
        ids[got] = veid;
        got ++;
    }

    return got;
}

static int openvzNumDomains(virConnectPtr conn) {
    return ovz_driver.num_active;
}

static int openvzListDefinedDomains(virConnectPtr conn,
                            char **const names, int nnames) {
    int got = 0;
    FILE *fp;
    int veid;
    char vpsname[OPENVZ_NAME_MAX];

    /* the -S options lists only stopped domains */
    if((fp = popen(VZLIST " -S -o vpsid -H 2> /dev/null", "r")) == NULL){
        error(conn, VIR_ERR_INTERNAL_ERROR, "Could not popen " VZLIST);
        return (int)NULL;
    }

    while(!(feof(fp)) && got < nnames){
        fscanf(fp, "%d\n", &veid);
        sprintf(vpsname, "%d", veid);
        names[got] = strdup(vpsname);
        got ++;
    }

    return got;
}


static int openvzNumDefinedDomains(virConnectPtr conn) {
    return ovz_driver.num_inactive; 
}

static int openvzStartup(void) {
    openvzAssignUUIDs();
    
    return 0;
}

static int openvzShutdown(void) {

    return 0;
}

static int openvzReload(void) {

    return 0;
}

static int openvzActive(void) {

    return 1;
}

static int openvzCloseNetwork(virConnectPtr conn) {
    return 0;
}

static virDrvOpenStatus openvzOpenNetwork(virConnectPtr conn,
                                         const char *name ATTRIBUTE_UNUSED,
                                         int flags ATTRIBUTE_UNUSED) {
    return VIR_DRV_OPEN_SUCCESS;
}


static virDriver openvzDriver = {
    VIR_DRV_OPENVZ,
    "OPENVZ",
    LIBVIR_VERSION_NUMBER,
    openvzOpen, /* open */
    openvzClose, /* close */
    openvzGetType, /* type */
    NULL, /* version */
    NULL, /* hostname */
    NULL, /* uri */
    NULL, /* getMaxVcpus */
    NULL, /* nodeGetInfo */
    NULL, /* getCapabilities */
    openvzListDomains, /* listDomains */
    openvzNumDomains, /* numOfDomains */
    NULL, /* domainCreateLinux */
    openvzDomainLookupByID, /* domainLookupByID */
    openvzDomainLookupByUUID, /* domainLookupByUUID */
    openvzDomainLookupByName, /* domainLookupByName */
    NULL, /* domainSuspend */
    NULL, /* domainResume */
    openvzDomainShutdown, /* domainShutdown */
    openvzDomainReboot, /* domainReboot */
    openvzDomainShutdown, /* domainDestroy */
    openvzGetOSType, /* domainGetOSType */
    NULL, /* domainGetMaxMemory */
    NULL, /* domainSetMaxMemory */
    NULL, /* domainSetMemory */
    openvzDomainGetInfo, /* domainGetInfo */
    NULL, /* domainSave */
    NULL, /* domainRestore */
    NULL, /* domainCoreDump */
    NULL, /* domainSetVcpus */
    NULL, /* domainPinVcpu */
    NULL, /* domainGetVcpus */
    NULL, /* domainGetMaxVcpus */
    NULL, /* domainDumpXML */
    openvzListDefinedDomains, /* listDomains */
    openvzNumDefinedDomains, /* numOfDomains */
    openvzDomainCreate, /* domainCreate */
    NULL, /* domainDefineXML */
    NULL, /* domainUndefine */
    NULL, /* domainAttachDevice */
    NULL, /* domainDetachDevice */
    NULL, /* domainGetAutostart */
    NULL, /* domainSetAutostart */
    NULL, /* domainGetSchedulerType */
    NULL, /* domainGetSchedulerParameters */
    NULL, /* domainSetSchedulerParameters */
};

static virNetworkDriver openvzNetworkDriver = {
    openvzOpenNetwork, /* open */
    openvzCloseNetwork, /* close */
    NULL, /* numOfNetworks */
    NULL, /* listNetworks */
    NULL, /* numOfDefinedNetworks */
    NULL, /* listDefinedNetworks */
    NULL, /* networkLookupByUUID */
    NULL, /* networkLookupByName */
    NULL, /* networkCreateXML */
    NULL, /* networkDefineXML */
    NULL, /* networkUndefine */
    NULL, /* networkCreate */
    NULL, /* networkDestroy */
    NULL, /* networkDumpXML */
    NULL, /* networkGetBridgeName */
    NULL, /* networkGetAutostart */
    NULL, /* networkSetAutostart */
};

static virStateDriver openvzStateDriver = {
    openvzStartup,
    openvzShutdown,
    openvzReload,
    openvzActive,
};

int openvzRegister(void) {
    virRegisterDriver(&openvzDriver);
    virRegisterNetworkDriver(&openvzNetworkDriver);
    virRegisterStateDriver(&openvzStateDriver);
    return 0;
}

#endif /* WITH_OPENVZ */

/*
 * Local variables:
 *  indent-tabs-mode: nil
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */
/*
 * openvz_driver.h: core driver methods for managing OpenVZ VPSs
 *
 * Copyright (C) 2006, 2007 Binary Karma.
 * Copyright (C) 2006 Shuveb Hussain
 *
 * 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
 *
 * Author: Shuveb Hussain <shuveb binarykarma com>
 */


#ifndef OPENVZ_DRIVER_H
#define OPENVZ_DRIVER_H

#include "internal.h"

/* OpenVZ commands - Replace with wrapper scripts later? */
#define VZLIST  "vzlist"
#define VZCTL   "vzctl"

struct openvz_driver {
    struct openvz_vm *vms;
    int num_active;
    int num_inactive;
};

int openvzRegister(void);

#endif


/*
 * Local variables:
 *  indent-tabs-mode: nil
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]