mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-20 03:16:12 +00:00
system: add snapshots (boot environments) GUI support #7749
This pull request introduces a new feature to the OPNsense web interface, allowing users to manage FreeBSD boot environments directly within OPNsense. This integration provides an intuitive and seamless way for users to create, manage, and switch between boot environments, enhancing system management and recovery options. Renamed the menu item to "Snapshots" in an attempt to explain the feature to non-FreeBSD users.
This commit is contained in:
parent
346e913323
commit
7118a82a05
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
* Copyright (C) 2024 Sheridan Computers Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
namespace OPNsense\Core\Api;
|
||||
|
||||
use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
use OPNsense\Base\UserException;
|
||||
|
||||
|
||||
class SnapshotsController extends ApiControllerBase
|
||||
{
|
||||
private array $environments = [];
|
||||
|
||||
/**
|
||||
* @param string $fieldname property to search
|
||||
* @param string $value value the property should have
|
||||
* @return array|null
|
||||
*/
|
||||
private function find($fieldname, $value)
|
||||
{
|
||||
if (empty($this->environments)) {
|
||||
$this->environments = json_decode(trim((new Backend())->configdRun('zfs snapshot list')), true) ?? [];
|
||||
}
|
||||
foreach ($this->environments as $record) {
|
||||
if (isset($record[$fieldname]) && $record[$fieldname] == $value) {
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uuid generated uuid to search (calculated by name)
|
||||
* @return array|null
|
||||
*/
|
||||
private function findByUuid($uuid)
|
||||
{
|
||||
return $this->find('uuid', $uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name snapshot name, the actual key of the record
|
||||
* @return array|null
|
||||
*/
|
||||
private function findByName($name)
|
||||
{
|
||||
return $this->find('name', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* allow all but whitespaces for now
|
||||
* @param string $name snapshot name
|
||||
*/
|
||||
private function isValidName($name)
|
||||
{
|
||||
return !preg_match('/\s/', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean is this a supported feature (ZFS enabled)
|
||||
*/
|
||||
public function isSupportedAction()
|
||||
{
|
||||
$result = json_decode((new Backend())->configdRun('zfs snapshot supported'), true) ?? [];
|
||||
return ['supported' => !empty($result) && $result['status'] == 'OK'];
|
||||
}
|
||||
|
||||
/**
|
||||
* search snapshots
|
||||
* @return array
|
||||
*/
|
||||
public function searchAction()
|
||||
{
|
||||
$records = json_decode((new Backend())->configdRun('zfs snapshot list'), true) ?? [];
|
||||
return $this->searchRecordsetBase($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch an environment by uuid, return new when not found or $uuid equals null
|
||||
* @param string $uuid
|
||||
* @return array
|
||||
*/
|
||||
public function getAction($uuid = null)
|
||||
{
|
||||
if (!empty($uuid)) {
|
||||
$result = $this->findByUuid($uuid);
|
||||
if (!empty($result)) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
// new or not found
|
||||
return ['name' => 'BE'.date("YmdHis"), 'uuid' => ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new snapshot
|
||||
* @param string $uuid uuid to save
|
||||
* @return array status
|
||||
*/
|
||||
public function setAction($uuid)
|
||||
{
|
||||
if ($this->request->isPost() && $this->request->hasPost('name')) {
|
||||
$name = $this->request->getPost('name', 'string', null);
|
||||
|
||||
$be = $this->findByUuid($uuid);
|
||||
$new_be = $this->findByName($name);
|
||||
|
||||
if (!empty($be) && $be['name'] == $name) {
|
||||
/* skip, unchanged */
|
||||
return ['status' => 'ok'];
|
||||
} elseif (!empty($be) && empty($new_be) && $this->isValidName($name)) {
|
||||
return json_decode(
|
||||
(new Backend())->configdpRun("zfs snapshot rename", [$be['name'], $name]),
|
||||
true
|
||||
);
|
||||
} else {
|
||||
if (!empty($new_be)) {
|
||||
$msg = gettext('A snapshot already exists by this name');
|
||||
} elseif (!$this->isValidName($name)){
|
||||
$msg = gettext('Invalid name specified');
|
||||
} else {
|
||||
$msg = gettext('Snapshot not found');
|
||||
}
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'validations' => [
|
||||
'name' => $msg
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* add or clone a snapshot
|
||||
* @return array status
|
||||
*/
|
||||
public function addAction()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$uuid = $this->request->getPost('uuid', 'string', '');
|
||||
$name = $this->request->getPost('name', 'string', '');
|
||||
|
||||
$msg = null;
|
||||
if ($this->findByName($name)) {
|
||||
$msg = gettext('A snapshot already exists by this name');
|
||||
} elseif (!$this->isValidName($name)){
|
||||
$msg = gettext('Invalid name specified');
|
||||
}
|
||||
if (!empty($uuid) && empty($msg)) {
|
||||
/* clone environment */
|
||||
$be = $this->findByUuid($uuid);
|
||||
if (empty($be)) {
|
||||
$msg = gettext('Snapshot not found');
|
||||
} else {
|
||||
return json_decode(
|
||||
(new Backend())->configdpRun("zfs snapshot clone", [$name, $be['name']]), true
|
||||
);
|
||||
}
|
||||
} elseif (empty($msg)) {
|
||||
return (new Backend())->configdpRun("zfs snapshot create", [$name]);
|
||||
}
|
||||
|
||||
if ($msg) {
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'validations' => [
|
||||
'name' => $msg
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* delete an environment by uuid
|
||||
* @param string $uuid
|
||||
* @return array
|
||||
* @throws UserException when not found (or possible)
|
||||
*/
|
||||
public function delAction($uuid)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$be = $this->findByUuid($uuid);
|
||||
if (empty($be)) {
|
||||
throw new UserException(gettext("Snapshot not found"), gettext("Snapshots"));
|
||||
}
|
||||
if ($be['active'] != '-') {
|
||||
throw new UserException(gettext("Cannot delete active snapshot"), gettext("Snapshots"));
|
||||
}
|
||||
return (json_decode((new Backend())->configdpRun("zfs snapshot destroy", [$be['name']]), true));
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
/**
|
||||
* activate a snapshot by uuid
|
||||
* @param string $uuid
|
||||
* @return array
|
||||
* @throws UserException when not found (or possible)
|
||||
*/
|
||||
public function activateAction($uuid)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$be = $this->findByUuid($uuid);
|
||||
if (empty($be)) {
|
||||
throw new UserException(gettext("Snapshot not found"), gettext("Snapshots"));
|
||||
}
|
||||
return json_decode((new Backend())->configdpRun("zfs snapshot activate", [$be['name']]), true);
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
* Copyright (C) 2024 Sheridan Computers Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
namespace OPNsense\Core;
|
||||
|
||||
|
||||
class SnapshotsController extends \OPNsense\Base\IndexController
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function templateJSIncludes()
|
||||
{
|
||||
return array_merge(parent::templateJSIncludes(), [
|
||||
'/ui/js/moment-with-locales.min.js'
|
||||
]);
|
||||
}
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->pick('OPNsense/Core/snapshot');
|
||||
$this->view->SnapshotForm = $this->getForm('snapshot');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>uuid</id>
|
||||
<label>UUID</label>
|
||||
<type>hidden</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>name</id>
|
||||
<label>Name</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
</form>
|
||||
@ -652,6 +652,13 @@
|
||||
<pattern>api/trust/crl/*</pattern>
|
||||
</patterns>
|
||||
</page-system-crlmanager>
|
||||
<page-snapshots>
|
||||
<name>System: Snapshots</name>
|
||||
<patterns>
|
||||
<pattern>ui/snapshots/*</pattern>
|
||||
<pattern>api/snapshots/*</pattern>
|
||||
</patterns>
|
||||
</page-snapshots>
|
||||
<page-system-firmware-manualupdate>
|
||||
<name>System: Firmware</name>
|
||||
<patterns>
|
||||
|
||||
@ -72,6 +72,7 @@
|
||||
<Edit url="/system_advanced_sysctl.php*" visibility="hidden"/>
|
||||
</Tunables>
|
||||
</Settings>
|
||||
<Snapshots cssClass="fa fa-hdd-o fa-fw" url="/ui/core/snapshots/"/>
|
||||
<Trust cssClass="fa fa-certificate fa-fw">
|
||||
<Authorities url="/ui/trust/ca"/>
|
||||
<Certificates url="/ui/trust/cert"/>
|
||||
|
||||
84
src/opnsense/mvc/app/views/OPNsense/Core/snapshot.volt
Normal file
84
src/opnsense/mvc/app/views/OPNsense/Core/snapshot.volt
Normal file
@ -0,0 +1,84 @@
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
/* always hide "uuid" row in edit form */
|
||||
$("#row_uuid").hide();
|
||||
/* before rendering anything, check if ZFS is supported */
|
||||
ajaxGet('/api/core/snapshots/is_supported/', {}, function(data, status) {
|
||||
if (data && data.supported) {
|
||||
$("#grid-env").UIBootgrid({
|
||||
get: '/api/core/snapshots/get/',
|
||||
set: '/api/core/snapshots/set/',
|
||||
add: '/api/core/snapshots/add/',
|
||||
del: '/api/core/snapshots/del/',
|
||||
search: '/api/core/snapshots/search',
|
||||
commands: {
|
||||
activate_be: {
|
||||
method: function(event) {
|
||||
let uuid = $(this).data("row-id") !== undefined ? $(this).data("row-id") : '';
|
||||
ajaxCall('/api/core/snapshots/activate/' + uuid, {}, function(data, status){
|
||||
if (data.result) {
|
||||
$('#grid-env').bootgrid('reload');
|
||||
}
|
||||
});
|
||||
},
|
||||
classname: 'fa fa-fw fa-check',
|
||||
title: "{{ lang._('Activate') }}",
|
||||
sequence: 10
|
||||
},
|
||||
},
|
||||
options: {
|
||||
selection: false,
|
||||
multiSelect: false,
|
||||
rowSelect: false,
|
||||
formatters: {
|
||||
"timestamp": function (column, row) {
|
||||
return moment.unix(row[column.id]).local().format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#supported_block").show();
|
||||
} else {
|
||||
$("#unsupported_block").show();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="supported_block" class="content-box" style="display: none;">
|
||||
<table id="grid-env" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="frmSnapshot">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="uuid" data-type="string" data-visible="false" data-identifier="true" data-sortable="false">{{ lang._('uuid') }}</th>
|
||||
<th data-column-id="name" data-type="string" data-visible="true" data-identifier="false">{{ lang._('Name') }}</th>
|
||||
<th data-column-id="active" data-type="string" data-visible="true" data-sortable="false">{{ lang._('Active') }}</th>
|
||||
<th data-column-id="mountpoint" data-type="string" data-visible="true">{{ lang._('Mountpoint') }}</th>
|
||||
<th data-column-id="size" data-type="string" data-visible="true" data-sortable="false">{{ lang._('Size') }}</th>
|
||||
<th data-column-id="created" data-type="string" data-formatter="timestamp">{{ lang._('Created') }}</th>
|
||||
<th data-column-id="commands" data-width="9em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="6"></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="unsupported_block" style="padding: 10px; display: none;">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<i class="fa fa-fw fa-warning"></i>
|
||||
{{ lang._('Snapshots are only available when a ZFS file system is used.') }}
|
||||
<br/>
|
||||
{{ lang._('For more information on how to migrate to ZFS, please refer to our documentation or support resources.') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ partial("layout_partials/base_dialog",['fields':SnapshotForm,'id':'frmSnapshot', 'label':lang._('Edit snapshot')])}}
|
||||
100
src/opnsense/scripts/system/bectl.py
Executable file
100
src/opnsense/scripts/system/bectl.py
Executable file
@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Copyright (c) 2024 Deciso B.V.
|
||||
Copyright (c) 2024 Sheridan Computers Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
------------------------------------------------------------------------------------------
|
||||
Simple wrapper around bectl shell command
|
||||
"""
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'action',
|
||||
help='action to perform, see bectl for details',
|
||||
choices=['is_supported', 'activate', 'create', 'clone', 'destroy', 'list', 'rename']
|
||||
)
|
||||
parser.add_argument('--beName', help='name of boot environment', type=str)
|
||||
parser.add_argument('--from-source', help='boot environment to clone', type=str)
|
||||
inputargs = parser.parse_args()
|
||||
|
||||
cmd = []
|
||||
error_msg = None
|
||||
if subprocess.run(['df', '-Tt', 'zfs', '/'], capture_output=True).returncode != 0:
|
||||
error_msg = 'Unsupported root filesystem'
|
||||
elif inputargs.action == 'is_supported':
|
||||
print(json.dumps({"status": "OK", "message": "File system is ZFS"}))
|
||||
elif inputargs.action == 'list':
|
||||
cmd = ['bectl', 'list', '-H']
|
||||
elif inputargs.action == 'activate' and inputargs.beName:
|
||||
cmd = ['bectl', 'activate', inputargs.beName]
|
||||
elif inputargs.action == 'create':
|
||||
name = inputargs.beName if inputargs.beName else "BE-{date:%Y%m%d%H%M%S}".format(date=datetime.datetime.now())
|
||||
cmd = ['bectl', 'create', name]
|
||||
elif inputargs.action == 'clone' and inputargs.from_source:
|
||||
name = inputargs.beName if inputargs.beName else "BE-{date:%Y%m%d%H%M%S}".format(date=datetime.datetime.now())
|
||||
cmd = ['bectl', 'create', '-e', inputargs.from_source, name]
|
||||
elif inputargs.action == 'destroy' and inputargs.beName:
|
||||
cmd = ['bectl', 'destroy', inputargs.beName]
|
||||
elif inputargs.action == 'rename' and inputargs.beName and inputargs.from_source:
|
||||
cmd = ['bectl', 'rename', inputargs.from_source, inputargs.beName]
|
||||
else:
|
||||
print(json.dumps({"status": "failed", "result": "Incomplete argument list"}))
|
||||
sys.exit(-1)
|
||||
|
||||
if error_msg:
|
||||
print(json.dumps({"status": "failed", "result": error_msg}))
|
||||
elif len(cmd) > 0:
|
||||
sp = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if sp.returncode != 0:
|
||||
print(json.dumps({"status": "failed", "result": sp.stderr.strip()}))
|
||||
elif inputargs.action == 'list':
|
||||
result = []
|
||||
for line in sp.stdout.split("\n"):
|
||||
parts = line.split("\t")
|
||||
if len(parts) >= 5:
|
||||
result.append({
|
||||
"uuid": str(uuid.uuid3(uuid.NAMESPACE_DNS, parts[0])),
|
||||
"name": parts[0],
|
||||
"active": parts[1],
|
||||
"mountpoint": parts[2],
|
||||
"size": parts[3],
|
||||
"created_str": parts[4],
|
||||
"created": time.mktime(datetime.datetime.strptime(parts[4], "%Y-%m-%d %H:%M").timetuple())
|
||||
})
|
||||
print(json.dumps(result))
|
||||
else:
|
||||
print(json.dumps({
|
||||
"status": "ok",
|
||||
"result": 'bootenvironment executed %s successfully' % inputargs.action
|
||||
}))
|
||||
|
||||
@ -11,3 +11,45 @@ parameters:%s
|
||||
type:script
|
||||
message:Scrubbing ZFS Pool %s
|
||||
description:ZFS pool scrub
|
||||
|
||||
[snapshot.list]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters:list
|
||||
type:script_output
|
||||
message:List snapshots
|
||||
|
||||
[snapshot.create]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters: create --beName %s
|
||||
type:script_output
|
||||
message:Creating snapshot %s
|
||||
|
||||
[snapshot.clone]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters: clone --beName %s --from-source %s
|
||||
type:script_output
|
||||
message:Cloning snapshot %s from %s
|
||||
|
||||
[snapshot.activate]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters: activate --beName %s
|
||||
type:script_output
|
||||
message:Activate snapshot %s
|
||||
|
||||
[snapshot.destroy]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters: destroy --beName %s
|
||||
type:script_output
|
||||
message:Delete snapshot %s
|
||||
|
||||
[snapshot.rename]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters: rename --from-source %s --beName %s
|
||||
type:script_output
|
||||
message:Rename snapshot %s to %s
|
||||
|
||||
[snapshot.supported]
|
||||
command:/usr/local/opnsense/scripts/system/bectl.py
|
||||
parameters: is_supported
|
||||
type:script_output
|
||||
message:Checking if ZFS is supported
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user