Пример #1
0
Файл: events.go Проект: vahe/lxd
func eventsSocket(r *http.Request, w http.ResponseWriter) error {
	listener := eventListener{}

	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "logging,operation"
	}

	c, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	listener.active = make(chan bool, 1)
	listener.connection = c
	listener.id = uuid.NewRandom().String()
	listener.messageTypes = strings.Split(typeStr, ",")

	eventsLock.Lock()
	eventListeners[listener.id] = &listener
	eventsLock.Unlock()

	shared.LogDebugf("New events listener: %s", listener.id)

	<-listener.active

	eventsLock.Lock()
	delete(eventListeners, listener.id)
	eventsLock.Unlock()

	listener.connection.Close()
	shared.LogDebugf("Disconnected events listener: %s", listener.id)

	return nil
}
Пример #2
0
Файл: devlxd.go Проект: vahe/lxd
func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		cred, err := getCred(unixConn)
		if err != nil {
			shared.LogDebugf("Error getting ucred for conn %s", err)
		} else {
			m.m[unixConn] = cred
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		delete(m.m, unixConn)
	case http.StateClosed:
		delete(m.m, unixConn)
	default:
		shared.LogDebugf("Unknown state for connection %s", state)
	}
}
Пример #3
0
func (op *operation) Connect(r *http.Request, w http.ResponseWriter) (chan error, error) {
	if op.class != operationClassWebsocket {
		return nil, fmt.Errorf("Only websocket operations can be connected")
	}

	if op.status != shared.Running {
		return nil, fmt.Errorf("Only running operations can be connected")
	}

	chanConnect := make(chan error, 1)

	op.lock.Lock()

	go func(op *operation, chanConnect chan error) {
		err := op.onConnect(op, r, w)
		if err != nil {
			chanConnect <- err

			shared.LogDebugf("Failed to handle %s operation: %s: %s", op.class.String(), op.id, err)
			return
		}

		chanConnect <- nil

		shared.LogDebugf("Handled %s operation: %s", op.class.String(), op.id)
	}(op, chanConnect)
	op.lock.Unlock()

	shared.LogDebugf("Connected %s operation: %s", op.class.String(), op.id)

	return chanConnect, nil
}
Пример #4
0
Файл: rsync.go Проект: vahe/lxd
// RsyncSend sets up the sending half of an rsync, to recursively send the
// directory pointed to by path over the websocket.
func RsyncSend(path string, conn *websocket.Conn) error {
	cmd, dataSocket, stderr, err := rsyncSendSetup(path)
	if dataSocket != nil {
		defer dataSocket.Close()
	}
	if err != nil {
		return err
	}

	readDone, writeDone := shared.WebsocketMirror(conn, dataSocket, dataSocket)

	output, err := ioutil.ReadAll(stderr)
	if err != nil {
		shared.LogDebugf("problem reading rsync stderr %s", err)
	}

	err = cmd.Wait()
	if err != nil {
		shared.LogDebugf("problem with rsync send of %s: %s: %s", path, err, string(output))
	}

	<-readDone
	<-writeDone

	return err
}
Пример #5
0
func containersPost(d *Daemon, r *http.Request) Response {
	shared.LogDebugf("Responding to container create")

	req := containerPostReq{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	if req.Name == "" {
		cs, err := dbContainersList(d.db, cTypeRegular)
		if err != nil {
			return InternalError(err)
		}

		i := 0
		for {
			i++
			req.Name = strings.ToLower(petname.Generate(2, "-"))
			if !shared.StringInSlice(req.Name, cs) {
				break
			}

			if i > 100 {
				return InternalError(fmt.Errorf("couldn't generate a new unique name after 100 tries"))
			}
		}
		shared.LogDebugf("No name provided, creating %s", req.Name)
	}

	if req.Devices == nil {
		req.Devices = shared.Devices{}
	}

	if req.Config == nil {
		req.Config = map[string]string{}
	}

	if strings.Contains(req.Name, shared.SnapshotDelimiter) {
		return BadRequest(fmt.Errorf("Invalid container name: '%s' is reserved for snapshots", shared.SnapshotDelimiter))
	}

	switch req.Source.Type {
	case "image":
		return createFromImage(d, &req)
	case "none":
		return createFromNone(d, &req)
	case "migration":
		return createFromMigration(d, &req)
	case "copy":
		return createFromCopy(d, &req)
	default:
		return BadRequest(fmt.Errorf("unknown source type %s", req.Source.Type))
	}
}
Пример #6
0
func deviceEventListener(d *Daemon) {
	chNetlinkCPU, chNetlinkNetwork, chUSB, err := deviceNetlinkListener()
	if err != nil {
		shared.LogErrorf("scheduler: couldn't setup netlink listener")
		return
	}

	for {
		select {
		case e := <-chNetlinkCPU:
			if len(e) != 2 {
				shared.LogErrorf("Scheduler: received an invalid cpu hotplug event")
				continue
			}

			if !cgCpusetController {
				continue
			}

			shared.LogDebugf("Scheduler: cpu: %s is now %s: re-balancing", e[0], e[1])
			deviceTaskBalance(d)
		case e := <-chNetlinkNetwork:
			if len(e) != 2 {
				shared.LogErrorf("Scheduler: received an invalid network hotplug event")
				continue
			}

			if !cgNetPrioController {
				continue
			}

			shared.LogDebugf("Scheduler: network: %s has been added: updating network priorities", e[0])
			deviceNetworkPriority(d, e[0])
			networkAutoAttach(d, e[0])
		case e := <-chUSB:
			deviceUSBEvent(d, e)
		case e := <-deviceSchedRebalance:
			if len(e) != 3 {
				shared.LogErrorf("Scheduler: received an invalid rebalance event")
				continue
			}

			if !cgCpusetController {
				continue
			}

			shared.LogDebugf("Scheduler: %s %s %s: re-balancing", e[0], e[1], e[2])
			deviceTaskBalance(d)
		}
	}
}
Пример #7
0
// RsyncRecv sets up the receiving half of the websocket to rsync (the other
// half set up by RsyncSend), putting the contents in the directory specified
// by path.
func RsyncRecv(path string, conn *websocket.Conn, writeWrapper func(io.WriteCloser) io.WriteCloser) error {
	cmd := exec.Command("rsync",
		"--server",
		"-vlogDtpre.iLsfx",
		"--numeric-ids",
		"--devices",
		"--partial",
		".",
		path)

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	writePipe := io.WriteCloser(stdin)
	if writeWrapper != nil {
		writePipe = writeWrapper(stdin)
	}

	readDone, writeDone := shared.WebsocketMirror(conn, writePipe, stdout)
	data, err2 := ioutil.ReadAll(stderr)
	if err2 != nil {
		shared.LogDebugf("error reading rsync stderr: %s", err2)
		return err2
	}

	err = cmd.Wait()
	if err != nil {
		shared.LogDebugf("rsync recv error for path %s: %s: %s", path, err, string(data))
	}

	<-readDone
	<-writeDone

	return err
}
Пример #8
0
func (op *operation) Run() (chan error, error) {
	if op.status != shared.Pending {
		return nil, fmt.Errorf("Only pending operations can be started")
	}

	chanRun := make(chan error, 1)

	op.lock.Lock()
	op.status = shared.Running

	if op.onRun != nil {
		go func(op *operation, chanRun chan error) {
			err := op.onRun(op)
			if err != nil {
				op.lock.Lock()
				op.status = shared.Failure
				op.err = SmartError(err).String()
				op.lock.Unlock()
				op.done()
				chanRun <- err

				shared.LogDebugf("Failure for %s operation: %s: %s", op.class.String(), op.id, err)

				_, md, _ := op.Render()
				eventSend("operation", md)
				return
			}

			op.lock.Lock()
			op.status = shared.Success
			op.lock.Unlock()
			op.done()
			chanRun <- nil

			op.lock.Lock()
			shared.LogDebugf("Success for %s operation: %s", op.class.String(), op.id)
			_, md, _ := op.Render()
			eventSend("operation", md)
			op.lock.Unlock()
		}(op, chanRun)
	}
	op.lock.Unlock()

	shared.LogDebugf("Started %s operation: %s", op.class.String(), op.id)
	_, md, _ := op.Render()
	eventSend("operation", md)

	return chanRun, nil
}
Пример #9
0
Файл: exec.go Проект: vahe/lxd
func (c *execCmd) sendTermSize(control *websocket.Conn) error {
	width, height, err := termios.GetSize(int(syscall.Stdout))
	if err != nil {
		return err
	}

	shared.LogDebugf("Window size is now: %dx%d", width, height)

	w, err := control.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}

	msg := shared.ContainerExecControl{}
	msg.Command = "window-resize"
	msg.Args = make(map[string]string)
	msg.Args["width"] = strconv.Itoa(width)
	msg.Args["height"] = strconv.Itoa(height)

	buf, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = w.Write(buf)

	w.Close()
	return err
}
Пример #10
0
/*
 * filesystemDetect returns the filesystem on which
 * the passed-in path sits
 */
func filesystemDetect(path string) (string, error) {
	fs := syscall.Statfs_t{}

	err := syscall.Statfs(path, &fs)
	if err != nil {
		return "", err
	}

	switch fs.Type {
	case filesystemSuperMagicBtrfs:
		return "btrfs", nil
	case filesystemSuperMagicZfs:
		return "zfs", nil
	case filesystemSuperMagicTmpfs:
		return "tmpfs", nil
	case filesystemSuperMagicExt4:
		return "ext4", nil
	case filesystemSuperMagicXfs:
		return "xfs", nil
	case filesystemSuperMagicNfs:
		return "nfs", nil
	default:
		shared.LogDebugf("Unknown backing filesystem type: 0x%x", fs.Type)
		return string(fs.Type), nil
	}
}
Пример #11
0
func (op *operation) UpdateMetadata(opMetadata interface{}) error {
	if op.status != shared.Pending && op.status != shared.Running {
		return fmt.Errorf("Only pending or running operations can be updated")
	}

	if op.readonly {
		return fmt.Errorf("Read-only operations can't be updated")
	}

	newMetadata, err := shared.ParseMetadata(opMetadata)
	if err != nil {
		return err
	}

	op.lock.Lock()
	op.updatedAt = time.Now()
	op.metadata = newMetadata
	op.lock.Unlock()

	shared.LogDebugf("Updated metadata for %s operation: %s", op.class.String(), op.id)
	_, md, _ := op.Render()
	eventSend("operation", md)

	return nil
}
Пример #12
0
Файл: db.go Проект: vahe/lxd
func dbExec(db *sql.DB, q string, args ...interface{}) (sql.Result, error) {
	for i := 0; i < 100; i++ {
		result, err := db.Exec(q, args...)
		if err == nil {
			return result, nil
		}
		if !isDbLockedError(err) {
			shared.LogDebugf("DbExec: query %q error %q", q, err)
			return nil, err
		}
		time.Sleep(100 * time.Millisecond)
	}

	shared.LogDebugf("DbExec: query %q args %q, DB still locked", q, args)
	shared.PrintStack()
	return nil, fmt.Errorf("DB is locked")
}
Пример #13
0
Файл: db.go Проект: vahe/lxd
/*
 * . q is the database query
 * . inargs is an array of interfaces containing the query arguments
 * . outfmt is an array of interfaces containing the right types of output
 *   arguments, i.e.
 *      var arg1 string
 *      var arg2 int
 *      outfmt := {}interface{}{arg1, arg2}
 *
 * The result will be an array (one per output row) of arrays (one per output argument)
 * of interfaces, containing pointers to the actual output arguments.
 */
func dbQueryScan(db *sql.DB, q string, inargs []interface{}, outfmt []interface{}) ([][]interface{}, error) {
	for i := 0; i < 100; i++ {
		result, err := doDbQueryScan(db, q, inargs, outfmt)
		if err == nil {
			return result, nil
		}
		if !isDbLockedError(err) {
			shared.LogDebugf("DbQuery: query %q error %q", q, err)
			return nil, err
		}
		time.Sleep(100 * time.Millisecond)
	}

	shared.LogDebugf("DbQueryscan: query %q inargs %q, DB still locked", q, inargs)
	shared.PrintStack()
	return nil, fmt.Errorf("DB is locked")
}
Пример #14
0
Файл: db.go Проект: vahe/lxd
func txCommit(tx *sql.Tx) error {
	for i := 0; i < 100; i++ {
		err := tx.Commit()
		if err == nil {
			return nil
		}
		if !isDbLockedError(err) {
			shared.LogDebugf("Txcommit: error %q", err)
			return err
		}
		time.Sleep(100 * time.Millisecond)
	}

	shared.LogDebugf("Txcommit: db still locked")
	shared.PrintStack()
	return fmt.Errorf("DB is locked")
}
Пример #15
0
Файл: db.go Проект: vahe/lxd
func dbBegin(db *sql.DB) (*sql.Tx, error) {
	for i := 0; i < 100; i++ {
		tx, err := db.Begin()
		if err == nil {
			return tx, nil
		}
		if !isDbLockedError(err) {
			shared.LogDebugf("DbBegin: error %q", err)
			return nil, err
		}
		time.Sleep(100 * time.Millisecond)
	}

	shared.LogDebugf("DbBegin: DB still locked")
	shared.PrintStack()
	return nil, fmt.Errorf("DB is locked")
}
Пример #16
0
Файл: debug.go Проект: vahe/lxd
func memProfiler(memProfile string) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGUSR1)
	for {
		sig := <-ch
		shared.LogDebugf("Received '%s signal', dumping memory.", sig)
		doMemDump(memProfile)
	}
}
Пример #17
0
func (c *execCmd) controlSocketHandler(d *lxd.Client, control *websocket.Conn) {
	// TODO: figure out what the equivalent of signal.SIGWINCH is on
	// windows and use that; for now if you resize your terminal it just
	// won't work quite correctly.
	err := c.sendTermSize(control)
	if err != nil {
		shared.LogDebugf("error setting term size %s", err)
	}
}
Пример #18
0
Файл: debug.go Проект: vahe/lxd
func doMemDump(memProfile string) {
	f, err := os.Create(memProfile)
	if err != nil {
		shared.LogDebugf("Error opening memory profile file '%s': %s", err)
		return
	}
	pprof.WriteHeapProfile(f)
	f.Close()
}
Пример #19
0
Файл: images.go Проект: vahe/lxd
func doDeleteImage(d *Daemon, fingerprint string) error {
	id, imgInfo, err := dbImageGet(d.db, fingerprint, false, false)
	if err != nil {
		return err
	}

	// get storage before deleting images/$fp because we need to
	// look at the path
	s, err := storageForImage(d, imgInfo)
	if err != nil {
		shared.LogError("error detecting image storage backend", log.Ctx{"fingerprint": imgInfo.Fingerprint, "err": err})
	} else {
		// Remove the image from storage backend
		if err = s.ImageDelete(imgInfo.Fingerprint); err != nil {
			shared.LogError("error deleting the image from storage backend", log.Ctx{"fingerprint": imgInfo.Fingerprint, "err": err})
		}
	}

	// Remove main image file
	fname := shared.VarPath("images", imgInfo.Fingerprint)
	if shared.PathExists(fname) {
		err = os.Remove(fname)
		if err != nil {
			shared.LogDebugf("Error deleting image file %s: %s", fname, err)
		}
	}

	// Remove the rootfs file
	fname = shared.VarPath("images", imgInfo.Fingerprint) + ".rootfs"
	if shared.PathExists(fname) {
		err = os.Remove(fname)
		if err != nil {
			shared.LogDebugf("Error deleting image file %s: %s", fname, err)
		}
	}

	// Remove the DB entry
	if err = dbImageDelete(d.db, id); err != nil {
		return err
	}

	return nil
}
Пример #20
0
func containersGet(d *Daemon, r *http.Request) Response {
	for i := 0; i < 100; i++ {
		result, err := doContainersGet(d, d.isRecursionRequest(r))
		if err == nil {
			return SyncResponse(true, result)
		}
		if !isDbLockedError(err) {
			shared.LogDebugf("DBERR: containersGet: error %q", err)
			return InternalError(err)
		}
		// 1 s may seem drastic, but we really don't want to thrash
		// perhaps we should use a random amount
		time.Sleep(100 * time.Millisecond)
	}

	shared.LogDebugf("DBERR: containersGet, db is locked")
	shared.PrintStack()
	return InternalError(fmt.Errorf("DB is locked"))
}
Пример #21
0
func operationCreate(opClass operationClass, opResources map[string][]string, opMetadata interface{},
	onRun func(*operation) error,
	onCancel func(*operation) error,
	onConnect func(*operation, *http.Request, http.ResponseWriter) error) (*operation, error) {

	// Main attributes
	op := operation{}
	op.id = uuid.NewRandom().String()
	op.class = opClass
	op.createdAt = time.Now()
	op.updatedAt = op.createdAt
	op.status = shared.Pending
	op.url = fmt.Sprintf("/%s/operations/%s", shared.APIVersion, op.id)
	op.resources = opResources
	op.chanDone = make(chan error)

	newMetadata, err := shared.ParseMetadata(opMetadata)
	if err != nil {
		return nil, err
	}
	op.metadata = newMetadata

	// Callback functions
	op.onRun = onRun
	op.onCancel = onCancel
	op.onConnect = onConnect

	// Sanity check
	if op.class != operationClassWebsocket && op.onConnect != nil {
		return nil, fmt.Errorf("Only websocket operations can have a Connect hook")
	}

	if op.class == operationClassWebsocket && op.onConnect == nil {
		return nil, fmt.Errorf("Websocket operations must have a Connect hook")
	}

	if op.class == operationClassToken && op.onRun != nil {
		return nil, fmt.Errorf("Token operations can't have a Run hook")
	}

	if op.class == operationClassToken && op.onCancel != nil {
		return nil, fmt.Errorf("Token operations can't have a Cancel hook")
	}

	operationsLock.Lock()
	operations[op.id] = &op
	operationsLock.Unlock()

	shared.LogDebugf("New %s operation: %s", op.class.String(), op.id)
	_, md, _ := op.Render()
	eventSend("operation", md)

	return &op, nil
}
Пример #22
0
// Stop stops the shared daemon.
func (d *Daemon) Stop() error {
	forceStop := false

	d.tomb.Kill(errStop)
	shared.LogInfof("Stopping REST API handler:")
	for _, socket := range []*Socket{d.TCPSocket, d.UnixSocket} {
		if socket == nil {
			continue
		}

		if socket.CloseOnExit {
			shared.LogInfo(" - closing socket", log.Ctx{"socket": socket.Socket.Addr()})
			socket.Socket.Close()
		} else {
			shared.LogInfo(" - skipping socket-activated socket", log.Ctx{"socket": socket.Socket.Addr()})
			forceStop = true
		}
	}

	if n, err := d.numRunningContainers(); err != nil || n == 0 {
		shared.LogInfof("Unmounting shmounts")

		syscall.Unmount(shared.VarPath("shmounts"), syscall.MNT_DETACH)

		shared.LogInfof("Done unmounting shmounts")
	} else {
		shared.LogDebugf("Not unmounting shmounts (containers are still running)")
	}

	shared.LogInfof("Closing the database")
	d.db.Close()

	shared.LogInfof("Stopping /dev/lxd handler")
	d.devlxd.Close()
	shared.LogInfof("Stopped /dev/lxd handler")

	shared.LogInfof("Saving simplestreams cache")
	imageSaveStreamCache()
	shared.LogInfof("Saved simplestreams cache")

	if d.MockMode || forceStop {
		return nil
	}

	err := d.tomb.Wait()
	if err == errStop {
		return nil
	}

	return err
}
Пример #23
0
Файл: rsync.go Проект: vahe/lxd
func rsyncWebsocket(path string, cmd *exec.Cmd, conn *websocket.Conn) error {
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	readDone, writeDone := shared.WebsocketMirror(conn, stdin, stdout)
	data, err2 := ioutil.ReadAll(stderr)
	if err2 != nil {
		shared.LogDebugf("error reading rsync stderr: %s", err2)
		return err2
	}

	err = cmd.Wait()
	if err != nil {
		shared.LogDebugf("rsync recv error for path %s: %s: %s", path, err, string(data))
	}

	<-readDone
	<-writeDone

	return err
}
Пример #24
0
func (p *patch) apply(d *Daemon) error {
	shared.LogDebugf("Applying patch: %s", p.name)

	err := p.run(p.name, d)
	if err != nil {
		return err
	}

	err = dbPatchesMarkApplied(d.db, p.name)
	if err != nil {
		return err
	}

	return nil
}
Пример #25
0
func (c *migrationFields) controlChannel() <-chan MigrationControl {
	ch := make(chan MigrationControl)
	go func() {
		msg := MigrationControl{}
		err := c.recv(&msg)
		if err != nil {
			shared.LogDebugf("Got error reading migration control socket %s", err)
			close(ch)
			return
		}
		ch <- msg
	}()

	return ch
}
Пример #26
0
func (u *dbUpdate) apply(currentVersion int, d *Daemon) error {
	// Get the current schema version

	shared.LogDebugf("Updating DB schema from %d to %d", currentVersion, u.version)

	err := u.run(currentVersion, u.version, d)
	if err != nil {
		return err
	}

	_, err = d.db.Exec("INSERT INTO schema (version, updated_at) VALUES (?, strftime(\"%s\"));", u.version)
	if err != nil {
		return err
	}

	return nil
}
Пример #27
0
func dbContainerConfigInsert(tx *sql.Tx, id int, config map[string]string) error {
	str := "INSERT INTO containers_config (container_id, key, value) values (?, ?, ?)"
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for k, v := range config {
		_, err := stmt.Exec(id, k, v)
		if err != nil {
			shared.LogDebugf("Error adding configuration item %s = %s to container %d",
				k, v, id)
			return err
		}
	}

	return nil
}
Пример #28
0
Файл: db.go Проект: vahe/lxd
func dbQueryRowScan(db *sql.DB, q string, args []interface{}, outargs []interface{}) error {
	for i := 0; i < 100; i++ {
		err := db.QueryRow(q, args...).Scan(outargs...)
		if err == nil {
			return nil
		}
		if isNoMatchError(err) {
			return err
		}
		if !isDbLockedError(err) {
			return err
		}
		time.Sleep(100 * time.Millisecond)
	}

	shared.LogDebugf("DbQueryRowScan: query %q args %q, DB still locked", q, args)
	shared.PrintStack()
	return fmt.Errorf("DB is locked")
}
Пример #29
0
func (op *operation) UpdateResources(opResources map[string][]string) error {
	if op.status != shared.Pending && op.status != shared.Running {
		return fmt.Errorf("Only pending or running operations can be updated")
	}

	if op.readonly {
		return fmt.Errorf("Read-only operations can't be updated")
	}

	op.lock.Lock()
	op.updatedAt = time.Now()
	op.resources = opResources
	op.lock.Unlock()

	shared.LogDebugf("Updated resources for %s operation: %s", op.class.String(), op.id)
	_, md, _ := op.Render()
	eventSend("operation", md)

	return nil
}
Пример #30
0
func dbUpdateFromV10(currentVersion int, version int, d *Daemon) error {
	if d.MockMode {
		// No need to move lxc to containers in mock runs,
		// dbUpdateFromV12 will then set the db version to 13
		return nil
	}

	if shared.PathExists(shared.VarPath("lxc")) {
		err := os.Rename(shared.VarPath("lxc"), shared.VarPath("containers"))
		if err != nil {
			return err
		}

		shared.LogDebugf("Restarting all the containers following directory rename")
		containersShutdown(d)
		containersRestart(d)
	}

	return nil
}