PR feedback
This commit is contained in:
parent
c304d9cc62
commit
e8380b8a12
@ -493,13 +493,6 @@ exports.snapshot = snapshot
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
rollingDeltaBackup = $coroutine ({vm, remote, tag, depth}) ->
|
||||
_remote = yield @getRemote remote
|
||||
if not _remote?.path?
|
||||
throw new Error "No such Remote #{remote}"
|
||||
if not _remote.enabled
|
||||
throw new Error "Backup remote #{remote} is disabled"
|
||||
if _remote.type == 'smb'
|
||||
throw new Error "Delta Backup is not supported for smb remotes"
|
||||
return yield @rollingDeltaVmBackup({
|
||||
vm,
|
||||
remoteId: remote,
|
||||
@ -628,11 +621,6 @@ exports.importBackup = importBackup
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
rollingBackup = $coroutine ({vm, remoteId, tag, depth, compress, onlyMetadata}) ->
|
||||
remote = yield @getRemote remoteId
|
||||
if not remote?.path?
|
||||
throw new Error "No such Remote #{remoteId}"
|
||||
if not remote.enabled
|
||||
throw new Error "Backup remote #{remoteId} is disabled"
|
||||
return yield @rollingBackupVm({
|
||||
vm,
|
||||
remoteId,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import eventToPromise from 'event-to-promise'
|
||||
|
||||
export default class RemoteHandlerAbstract {
|
||||
constructor (remote) {
|
||||
this._remote = remote
|
||||
@ -8,42 +10,92 @@ export default class RemoteHandlerAbstract {
|
||||
}
|
||||
|
||||
async sync () {
|
||||
return await this._sync()
|
||||
}
|
||||
|
||||
async _sync () {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async forget () {
|
||||
return await this._forget()
|
||||
}
|
||||
|
||||
async _forget () {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async outputFile (file, data, options) {
|
||||
throw new Error('Not implemented')
|
||||
return await this._outputFile(file, data, options)
|
||||
}
|
||||
|
||||
async _outputFile (file, data, options) {
|
||||
const stream = this.createOutputStream(file)
|
||||
const promise = eventToPromise(stream, 'finish')
|
||||
stream.end(data)
|
||||
return promise
|
||||
}
|
||||
|
||||
async readFile (file, options) {
|
||||
throw new Error('Not implemented')
|
||||
return await this._readFile(file, options)
|
||||
}
|
||||
|
||||
async _readFile (file, options) {
|
||||
const stream = this.createReadStream(file, options)
|
||||
let data = ''
|
||||
stream.on('data', d => data += d)
|
||||
await eventToPromise(stream, 'end')
|
||||
return data
|
||||
}
|
||||
|
||||
async rename (oldPath, newPath) {
|
||||
return await this._rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
async _rename (oldPath, newPath) {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async list (dir = undefined) {
|
||||
async list (dir = '.') {
|
||||
return await this._list(dir)
|
||||
}
|
||||
|
||||
async _list (dir = '.') {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async createReadStream (file) {
|
||||
async createReadStream (file, options) {
|
||||
const length = await this.getSize(file)
|
||||
const stream = await this._createReadStream(file)
|
||||
stream.length = length
|
||||
return stream
|
||||
}
|
||||
|
||||
async _createReadStream (file, options) {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async createOutputStream (file) {
|
||||
async createOutputStream (file, options) {
|
||||
return await this._createOutputStream(file)
|
||||
}
|
||||
|
||||
async _createOutputStream (file, options) {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async unlink (file) {
|
||||
return await this._unlink(file)
|
||||
}
|
||||
|
||||
async _unlink (file) {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
async getSize (file) {
|
||||
return await this._getSize(file)
|
||||
}
|
||||
|
||||
async _getSize (file) {
|
||||
throw new Error('Not implement')
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
import fs from 'fs-promise'
|
||||
import RemoteHandlerAbstract from './abstract'
|
||||
import {dirname} from 'path'
|
||||
import startsWith from 'lodash.startswith'
|
||||
import {noop} from '../utils'
|
||||
import {resolve} from 'path'
|
||||
|
||||
export default class LocalHandler extends RemoteHandlerAbstract {
|
||||
constructor (remote) {
|
||||
super(remote)
|
||||
this.forget = noop
|
||||
}
|
||||
|
||||
_getFilePath (file) {
|
||||
const parts = [this._remote.path]
|
||||
if (file) {
|
||||
parts.push(file)
|
||||
}
|
||||
return parts.join('/')
|
||||
const path = resolve.apply(null, parts)
|
||||
if (!startsWith(path, this._remote.path)) {
|
||||
throw new Error('Remote path is unavailable')
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
async sync () {
|
||||
async _sync () {
|
||||
if (this._remote.enabled) {
|
||||
try {
|
||||
await fs.ensureDir(this._remote.path)
|
||||
@ -30,39 +30,39 @@ export default class LocalHandler extends RemoteHandlerAbstract {
|
||||
return this._remote
|
||||
}
|
||||
|
||||
async outputFile (file, data, options) {
|
||||
const path = this._getFilePath(file)
|
||||
await fs.ensureDir(dirname(path))
|
||||
await fs.writeFile(path, data, options)
|
||||
async _forget () {
|
||||
return noop()
|
||||
}
|
||||
|
||||
async readFile (file, options) {
|
||||
async _outputFile (file, data, options) {
|
||||
await fs.outputFile(this._getFilePath(file), data, options)
|
||||
}
|
||||
|
||||
async _readFile (file, options) {
|
||||
return await fs.readFile(this._getFilePath(file), options)
|
||||
}
|
||||
|
||||
async rename (oldPath, newPath) {
|
||||
async _rename (oldPath, newPath) {
|
||||
return await fs.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
|
||||
}
|
||||
|
||||
async list (dir = undefined) {
|
||||
async _list (dir = '.') {
|
||||
return await fs.readdir(this._getFilePath(dir))
|
||||
}
|
||||
|
||||
async createReadStream (file) {
|
||||
return fs.createReadStream(this._getFilePath(file))
|
||||
async _createReadStream (file, options) {
|
||||
return await fs.createReadStream(this._getFilePath(file), options)
|
||||
}
|
||||
|
||||
async createOutputStream (file, options) {
|
||||
const path = this._getFilePath(file)
|
||||
await fs.ensureDir(dirname(path))
|
||||
return fs.createWriteStream(path, options)
|
||||
async _createOutputStream (file, options) {
|
||||
return await fs.createOutputStream(this._getFilePath(file), options)
|
||||
}
|
||||
|
||||
async unlink (file) {
|
||||
return fs.unlink(this._getFilePath(file))
|
||||
async _unlink (file) {
|
||||
return await fs.unlink(this._getFilePath(file))
|
||||
}
|
||||
|
||||
async getSize (file) {
|
||||
async _getSize (file) {
|
||||
const stats = await fs.stat(this._getFilePath(file))
|
||||
return stats.size
|
||||
}
|
||||
|
@ -1,20 +1,11 @@
|
||||
import fs from 'fs-promise'
|
||||
import RemoteHandlerAbstract from './abstract'
|
||||
import {dirname} from 'path'
|
||||
import LocalHandler from './local'
|
||||
import {exec} from 'child_process'
|
||||
import {forEach, promisify} from '../utils'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export default class NfsHandler extends RemoteHandlerAbstract {
|
||||
_getFilePath (file) {
|
||||
const parts = [this._remote.path]
|
||||
if (file) {
|
||||
parts.push(file)
|
||||
}
|
||||
return parts.join('/')
|
||||
}
|
||||
|
||||
export default class NfsHandler extends LocalHandler {
|
||||
async _loadRealMounts () {
|
||||
let stdout
|
||||
try {
|
||||
@ -48,7 +39,7 @@ export default class NfsHandler extends RemoteHandlerAbstract {
|
||||
return await execAsync(`mount -t nfs ${remote.host}:${remote.share} ${remote.path}`)
|
||||
}
|
||||
|
||||
async sync () {
|
||||
async _sync () {
|
||||
await this._loadRealMounts()
|
||||
if (this._matchesRealMount(this._remote) && !this._remote.enabled) {
|
||||
try {
|
||||
@ -68,7 +59,7 @@ export default class NfsHandler extends RemoteHandlerAbstract {
|
||||
return this._remote
|
||||
}
|
||||
|
||||
async forget () {
|
||||
async _forget () {
|
||||
try {
|
||||
await this._umount(this._remote)
|
||||
} catch (_) {
|
||||
@ -79,41 +70,4 @@ export default class NfsHandler extends RemoteHandlerAbstract {
|
||||
async _umount (remote) {
|
||||
await execAsync(`umount ${remote.path}`)
|
||||
}
|
||||
|
||||
async outputFile (file, data, options) {
|
||||
const path = this._getFilePath(file)
|
||||
await fs.ensureDir(dirname(path))
|
||||
await fs.writeFile(path, data, options)
|
||||
}
|
||||
|
||||
async readFile (file, options) {
|
||||
return await fs.readFile(this._getFilePath(file), options)
|
||||
}
|
||||
|
||||
async rename (oldPath, newPath) {
|
||||
return await fs.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
|
||||
}
|
||||
|
||||
async list (dir = undefined) {
|
||||
return await fs.readdir(this._getFilePath(dir))
|
||||
}
|
||||
|
||||
async createReadStream (file) {
|
||||
return fs.createReadStream(this._getFilePath(file))
|
||||
}
|
||||
|
||||
async createOutputStream (file, options) {
|
||||
const path = this._getFilePath(file)
|
||||
await fs.ensureDir(dirname(path))
|
||||
return fs.createWriteStream(path, options)
|
||||
}
|
||||
|
||||
async unlink (file) {
|
||||
return fs.unlink(this._getFilePath(file))
|
||||
}
|
||||
|
||||
async getSize (file) {
|
||||
const stats = await fs.stat(this._getFilePath(file))
|
||||
return stats.size
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import RemoteHandlerAbstract from './abstract'
|
||||
export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
constructor (remote) {
|
||||
super(remote)
|
||||
this.forget = noop
|
||||
this._forget = noop
|
||||
}
|
||||
|
||||
_getClient (remote) {
|
||||
@ -19,6 +19,9 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
}
|
||||
|
||||
_getFilePath (file) {
|
||||
if (file === '.') {
|
||||
file = undefined
|
||||
}
|
||||
const parts = []
|
||||
if (this._remote.path !== '') {
|
||||
parts.push(this._remote.path)
|
||||
@ -29,13 +32,13 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
return parts.join('\\')
|
||||
}
|
||||
|
||||
_getDirname (file) {
|
||||
_dirname (file) {
|
||||
const parts = file.split('\\')
|
||||
parts.pop()
|
||||
return parts.join('\\')
|
||||
}
|
||||
|
||||
async sync () {
|
||||
async _sync () {
|
||||
if (this._remote.enabled) {
|
||||
try {
|
||||
// Check access (smb2 does not expose connect in public so far...)
|
||||
@ -48,10 +51,10 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
return this._remote
|
||||
}
|
||||
|
||||
async outputFile (file, data, options) {
|
||||
async _outputFile (file, data, options) {
|
||||
const client = this._getClient(this._remote)
|
||||
const path = this._getFilePath(file)
|
||||
const dir = this._getDirname(path)
|
||||
const dir = this._dirname(path)
|
||||
try {
|
||||
if (dir) {
|
||||
await client.ensureDir(dir)
|
||||
@ -62,7 +65,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
async readFile (file, options) {
|
||||
async _readFile (file, options) {
|
||||
const client = this._getClient(this._remote)
|
||||
try {
|
||||
return await client.readFile(this._getFilePath(file), options)
|
||||
@ -71,7 +74,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
async rename (oldPath, newPath) {
|
||||
async _rename (oldPath, newPath) {
|
||||
const client = this._getClient(this._remote)
|
||||
try {
|
||||
return await client.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
|
||||
@ -80,7 +83,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
async list (dir = undefined) {
|
||||
async _list (dir = '.') {
|
||||
const client = this._getClient(this._remote)
|
||||
try {
|
||||
return await client.readdir(this._getFilePath(dir))
|
||||
@ -89,23 +92,23 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
async createReadStream (file) {
|
||||
async _createReadStream (file, options) {
|
||||
const client = this._getClient(this._remote)
|
||||
const stream = await client.createReadStream(this._getFilePath(file))
|
||||
const stream = await client.createReadStream(this._getFilePath(file), options) // FIXME ensure that options are properly handled by @marsaud/smb2
|
||||
stream.on('end', () => client.close())
|
||||
return stream
|
||||
}
|
||||
|
||||
async createOutputStream (file, options) {
|
||||
async _createOutputStream (file, options) {
|
||||
const client = this._getClient(this._remote)
|
||||
const path = this._getFilePath(file)
|
||||
const dir = this._getDirname(path)
|
||||
const dir = this._dirname(path)
|
||||
let stream
|
||||
try {
|
||||
if (dir) {
|
||||
await client.ensureDir(dir)
|
||||
}
|
||||
stream = await client.createWriteStream(path, options/* , { flags: 'wx' }*/) // TODO ensure that wx flag is properly handled by @marsaud/smb2
|
||||
stream = await client.createWriteStream(path, options) // FIXME ensure that options are properly handled by @marsaud/smb2
|
||||
} catch (err) {
|
||||
client.close()
|
||||
throw err
|
||||
@ -114,7 +117,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
return stream
|
||||
}
|
||||
|
||||
async unlink (file) {
|
||||
async _unlink (file) {
|
||||
const client = this._getClient(this._remote)
|
||||
try {
|
||||
return await client.unlink(this._getFilePath(file))
|
||||
@ -123,7 +126,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
async getSize (file) {
|
||||
async _getSize (file) {
|
||||
const client = await this._getClient(this._remote)
|
||||
try {
|
||||
return await client.getSize(this._getFilePath(file))
|
||||
|
@ -378,6 +378,17 @@ export default class {
|
||||
|
||||
async rollingDeltaVmBackup ({vm, remoteId, tag, depth}) {
|
||||
const remote = await this._xo.getRemote(remoteId)
|
||||
|
||||
if (!remote) {
|
||||
throw new Error(`No such Remote ${remoteId}`)
|
||||
}
|
||||
if (!remote.enabled) {
|
||||
throw new Error(`Remote ${remoteId} is disabled`)
|
||||
}
|
||||
if (remote.type === 'smb') {
|
||||
throw new Error('Delta Backup is not supported for smb remotes')
|
||||
}
|
||||
|
||||
const dir = `vm_delta_${tag}_${vm.uuid}`
|
||||
|
||||
const info = {
|
||||
@ -589,6 +600,14 @@ export default class {
|
||||
|
||||
async rollingBackupVm ({vm, remoteId, tag, depth, compress, onlyMetadata}) {
|
||||
const remote = await this._xo.getRemote(remoteId)
|
||||
|
||||
if (!remote) {
|
||||
throw new Error(`No such Remote s{remoteId}`)
|
||||
}
|
||||
if (!remote.enabled) {
|
||||
throw new Error(`Backup remote ${remoteId} is disabled`)
|
||||
}
|
||||
|
||||
const handler = this._xo.getRemoteHandler(remote)
|
||||
const files = await handler.list()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user