feature/node-rewrite
Sven Slootweg 1 year ago
parent 87c95dc60a
commit ea9a7b2c43

2
.gitignore vendored

@ -4,3 +4,5 @@ images
disks disks
yarn-error.log yarn-error.log
junk junk
virtual-disks/vdisk1
virtual-disks/vdisk2

@ -0,0 +1,4 @@
root
hardware
blockDevices

@ -12,11 +12,12 @@
"url": "git@git.cryto.net:cvm" "url": "git@git.cryto.net:cvm"
}, },
"author": "Sven Slootweg", "author": "Sven Slootweg",
"license": "WTFPL", "license": "WTFPL OR CC0-1.0",
"dependencies": { "dependencies": {
"@babel/register": "^7.8.3", "@babel/register": "^7.8.3",
"@invisible/pegjs-import": "^1.1.1", "@invisible/pegjs-import": "^1.1.1",
"@joepie91/express-react-views": "^1.0.1", "@joepie91/express-react-views": "^1.0.1",
"@joepie91/result": "^0.1.0",
"@joepie91/unreachable": "^1.0.0", "@joepie91/unreachable": "^1.0.0",
"@validatem/allow-extra-properties": "^0.1.0", "@validatem/allow-extra-properties": "^0.1.0",
"@validatem/anything": "^0.1.0", "@validatem/anything": "^0.1.0",
@ -57,6 +58,7 @@
"debounce": "^1.0.0", "debounce": "^1.0.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"default-value": "^1.0.0", "default-value": "^1.0.0",
"dlayer": "^0.1.0",
"dotty": "^0.1.0", "dotty": "^0.1.0",
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"entities": "^2.0.0", "entities": "^2.0.0",
@ -78,6 +80,7 @@
"map-obj": "^4.2.1", "map-obj": "^4.2.1",
"match-value": "^1.1.0", "match-value": "^1.1.0",
"memoizee": "^0.4.14", "memoizee": "^0.4.14",
"merge-by-template": "^0.1.4",
"nanoid": "^2.1.11", "nanoid": "^2.1.11",
"object.fromentries": "^2.0.2", "object.fromentries": "^2.0.2",
"pegjs": "^0.10.0", "pegjs": "^0.10.0",

@ -139,4 +139,30 @@ table.drives th.unknown {
color: gray; color: gray;
} }
.volumeGroup {
max-width: 960px;
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
gap: 0.2em 0.4em;
}
.bar {
border: 1px solid black;
display: grid;
}
.barSegment {
box-sizing: border-box;
font-size: 0.8em;
background-color: rgb(205, 205, 205);
padding: 0.3em 0.4em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.barSegment:nth-child(even) {
background-color: rgb(177, 177, 177);
}
/*# sourceMappingURL=style.css.map */ /*# sourceMappingURL=style.css.map */

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../src/scss/style.scss"],"names":[],"mappings":"AAIA;EACC,kBALqB;EAMrB;EACA;;;AAGD;EACC;;;AAOD;EACC;;;AAGD;EACC,kBAtBqB;;AAwBrB;EACC;EACA;;AAGD;EACC;;AAIA;EACC;EACA;EACA;;AAIA;AAEC;EACA,kBA3CqB;EA4CrB;;AAKD;EACC;EACA;;;AAMJ;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAKD;EACC,kBArFmB;EAsFnB;EACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;;AAKD;EACC;;AAIA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAIF;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC","file":"style.css"} {"version":3,"sourceRoot":"","sources":["../../src/scss/style.scss"],"names":[],"mappings":"AAIA;EACC,kBALqB;EAMrB;EACA;;;AAGD;EACC;;;AAOD;EACC;;;AAGD;EACC,kBAtBqB;;AAwBrB;EACC;EACA;;AAGD;EACC;;AAIA;EACC;EACA;EACA;;AAIA;AAEC;EACA,kBA3CqB;EA4CrB;;AAKD;EACC;EACA;;;AAMJ;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAKD;EACC,kBArFmB;EAsFnB;EACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;;AAKD;EACC;;AAIA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAIF;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC","file":"style.css"}

@ -29,6 +29,7 @@ function makeListCommand({ command, selectResult, selectID }) {
if (id === All) { if (id === All) {
return items; return items;
} else { } else {
// TODO: Can this be more performant? Currently it is a nested loop
return items.find((item) => selectID(item) === id); return items.find((item) => selectID(item) === id);
} }
}); });

@ -1,10 +1,16 @@
"use strict"; "use strict";
const Promise = require("bluebird"); const Promise = require("bluebird");
const dlayer = require("../packages/dlayer"); const dlayer = require("dlayer");
const All = require("../packages/graphql-interface/symbols/all"); const All = require("../packages/graphql-interface/symbols/all");
const loaders = require("./data-sources"); const loaders = require("./data-sources");
const types = require("./types"); const types = require("./types");
const execLVM = require("../packages/exec-lvm");
const { validateValue } = require("@validatem/core");
const isString = require("@validatem/is-string");
const isBoolean = require("@validatem/is-boolean");
const required = require("@validatem/required");
const defaultTo = require("@validatem/default-to");
function typeFromSource(source, ids, factoryFunction) { function typeFromSource(source, ids, factoryFunction) {
return Promise.try(() => { return Promise.try(() => {
@ -36,15 +42,35 @@ module.exports = function () {
return typeFromSource(sources.lsblk, names, (device) => types.BlockDevice({ name: device.name })); return typeFromSource(sources.lsblk, names, (device) => types.BlockDevice({ name: device.name }));
}, },
lvm: { lvm: {
physicalVolumes: { physicalVolumes: ({ paths }, { sources }) => {
$get: ({ paths }, { sources }) => {
return typeFromSource(sources.lvmPhysicalVolumes, paths, (volume) => types.LVMPhysicalVolume({ path: volume.path })); return typeFromSource(sources.lvmPhysicalVolumes, paths, (volume) => types.LVMPhysicalVolume({ path: volume.path }));
}, },
$methods: {}
},
volumeGroups: ({ names }, { sources }) => { volumeGroups: ({ names }, { sources }) => {
return typeFromSource(sources.lvmVolumeGroups, names, (group) => types.LVMVolumeGroup({ name: group.name })); return typeFromSource(sources.lvmVolumeGroups, names, (group) => types.LVMVolumeGroup({ name: group.name }));
},
logicalVolumes: ({ paths }, { sources }) => {
return typeFromSource(sources.lvmLogicalVolumes, paths, (volume) => types.LVMPhysicalVolume({ path: volume.path }));
},
createPhysicalVolume: (args, { sources }) => {
let { path, force } = validateValue(args, {
path: [ required, isString ],
force: [ defaultTo(false), isBoolean ]
});
return Promise.try(() => {
return execLVM.createPhysicalVolume({ devicePath: path, force: force });
}).then(() => {
return types.LVMPhysicalVolume({ path });
});
},
createVolumeGroup: ({ name, physicalVolumes }) => {
return Promise.try(() => {
return execLVM.createVolumeGroup({ name, physicalVolumes });
}).then(() => {
return types.LVMVolumeGroup({ name });
});
} }
// createLogicalVolume: ,
}, },
images: { images: {
installationMedia: [], installationMedia: [],

@ -13,9 +13,11 @@ module.exports = function LVMPhysicalVolume ({ path }) {
format: "format", format: "format",
totalSpace: "totalSpace", totalSpace: "totalSpace",
freeSpace: "freeSpace", freeSpace: "freeSpace",
status: "status",
isExported: "isExported", isExported: "isExported",
isMissing: "isMissing", isMissing: "isMissing",
isAllocatable: "isAllocatable",
isDuplicate: "isDuplicate",
isUsed: "isUsed",
volumeGroup: (volume) => types.LVMVolumeGroup({ name: volume.volumeGroup }) volumeGroup: (volume) => types.LVMVolumeGroup({ name: volume.volumeGroup })
} }
} }

@ -39,6 +39,7 @@ module.exports = function () {
return api.query(template.query, queryArguments); return api.query(template.query, queryArguments);
} }
}).then((result) => { }).then((result) => {
console.dir({ templateInput: result }, { depth: null });
if (result == null) { if (result == null) {
return {}; return {};
} else { } else {

@ -4,7 +4,7 @@ const Promise = require("bluebird");
const mapObject = require("map-obj"); const mapObject = require("map-obj");
const syncpipe = require("syncpipe"); const syncpipe = require("syncpipe");
const Result = require("../result"); const Result = require("../result-old");
const createCursor = require("./cursor"); const createCursor = require("./cursor");
const deepMerge = require("./deep-merge"); const deepMerge = require("./deep-merge");
const loadModules = require("./load-modules"); const loadModules = require("./load-modules");
@ -226,7 +226,7 @@ function makeEnvironment(context, getContextForModule) {
module.exports = function createDLayer(options) { module.exports = function createDLayer(options) {
// options = { schema, makeContext } // options = { schema, makeContext }
let loaded = loadModules(options.modules); let loaded = loadModules(options.modules ?? []);
let schema = deepMerge(loaded.root, options.schema); let schema = deepMerge(loaded.root, options.schema);
return { return {

@ -95,11 +95,11 @@ module.exports = function (modules) {
let schemaRoots = modules.map((module) => module.root ?? {}); let schemaRoots = modules.map((module) => module.root ?? {});
for (let module of modules) { for (let module of modules) {
for (let [ type, factory ] of Object.entries(module.types)) { for (let [ type, factory ] of Object.entries(module.types ?? {})) {
types.add(module, type, factory); types.add(module, type, factory);
} }
for (let [ type, extensions ] of Object.entries(module.extensions)) { for (let [ type, extensions ] of Object.entries(module.extensions ?? {})) {
for (let [ name, method ] of Object.entries(extensions)) { for (let [ name, method ] of Object.entries(extensions)) {
typeExtensions.add(module, type, name, method); typeExtensions.add(module, type, name, method);
} }

@ -1,7 +1,7 @@
"use strict"; "use strict";
const Promise = require("bluebird"); const Promise = require("bluebird");
const dlayer = require("./"); const dlayer = require(".");
const loaders = require("../../api/data-sources"); const loaders = require("../../api/data-sources");

@ -3,7 +3,7 @@
const Promise = require("bluebird"); const Promise = require("bluebird");
const syncpipe = require("syncpipe"); const syncpipe = require("syncpipe");
const util = require("util"); const util = require("util");
const Result = require("../result"); const Result = require("@joepie91/result");
const ID = Symbol("dlayer-source object ID"); const ID = Symbol("dlayer-source object ID");
const AllowErrors = Symbol("dlayer-source allow-errors marker"); const AllowErrors = Symbol("dlayer-source allow-errors marker");

@ -1,8 +1,8 @@
"use strict"; "use strict";
const Promise = require("bluebird"); const Promise = require("bluebird");
const dlayer = require("../dlayer"); const dlayer = require("dlayer");
const Result = require("../result"); const Result = require("@joepie91/result");
module.exports = function dlayerWrap(callback, options = {}) { module.exports = function dlayerWrap(callback, options = {}) {
return Promise.try(() => { return Promise.try(() => {

@ -8,6 +8,7 @@ const unattendedFlags = require("../modifiers/unattended-flags");
const handleDeviceNotFound = require("../modifiers/handle-device-not-found"); const handleDeviceNotFound = require("../modifiers/handle-device-not-found");
const handlePartitionExists = require("../modifiers/handle-partition-exists"); const handlePartitionExists = require("../modifiers/handle-partition-exists");
const handleDeviceInUse = require("../modifiers/handle-device-in-use"); const handleDeviceInUse = require("../modifiers/handle-device-in-use");
const handleDeviceTooSmall = require("../modifiers/handle-device-too-small");
module.exports = function ({ devicePath, force }) { module.exports = function ({ devicePath, force }) {
return Promise.try(() => { return Promise.try(() => {
@ -17,6 +18,7 @@ module.exports = function ({ devicePath, force }) {
.withModifier((force === true) ? forceFlags : unattendedFlags) .withModifier((force === true) ? forceFlags : unattendedFlags)
.withModifier(handleDeviceNotFound(devicePath)) .withModifier(handleDeviceNotFound(devicePath))
.withModifier(handleDeviceInUse(devicePath)) .withModifier(handleDeviceInUse(devicePath))
.withModifier(handleDeviceTooSmall(devicePath))
.withModifier(handlePartitionExists(devicePath, "create a Physical Volume")) .withModifier(handlePartitionExists(devicePath, "create a Physical Volume"))
.execute(); .execute();
}).then((_output) => { }).then((_output) => {

@ -11,6 +11,9 @@ module.exports = function () {
return Promise.try(() => { return Promise.try(() => {
return execBinary("pvs") return execBinary("pvs")
.asRoot() .asRoot()
.withFlags({
options: "pv_all,vg_name"
})
.withModifier(asJson((result) => { .withModifier(asJson((result) => {
return { return {
volumes: result.report[0].pv.map((volume) => { volumes: result.report[0].pv.map((volume) => {
@ -21,11 +24,9 @@ module.exports = function () {
// FIXME: These amounts can contain commas depending on locale (eg. https://serverfault.com/a/648302) // FIXME: These amounts can contain commas depending on locale (eg. https://serverfault.com/a/648302)
totalSpace: parseIECBytes(volume.pv_size), totalSpace: parseIECBytes(volume.pv_size),
freeSpace: parseIECBytes(volume.pv_free), freeSpace: parseIECBytes(volume.pv_free),
status: mapFlag(volume.pv_attr, 0, { isAllocatable: (volume.pv_allocatable === "allocatable"),
d: "DUPLICATE", isDuplicate: (volume.pv_duplicate === "duplicate"),
a: "ALLOCATABLE", isUsed: (volume.pv_in_use === "used"),
u: "USED"
}),
isExported: mapFlag(volume.pv_attr, 1, { isExported: mapFlag(volume.pv_attr, 1, {
x: true, x: true,
"-": false "-": false

@ -10,5 +10,6 @@ module.exports = {
InvalidVolumeGroup: errorChain.create("InvalidVolumeGroup"), InvalidVolumeGroup: errorChain.create("InvalidVolumeGroup"),
PhysicalVolumeInUse: errorChain.create("PhysicalVolumeInUse"), PhysicalVolumeInUse: errorChain.create("PhysicalVolumeInUse"),
DeviceInUse: errorChain.create("PhysicalVolumeInUse"), DeviceInUse: errorChain.create("PhysicalVolumeInUse"),
IncompatibleDevice: errorChain.create("IncompatibleDevice") IncompatibleDevice: errorChain.create("IncompatibleDevice"),
DeviceTooSmall: errorChain.create("DeviceTooSmall"),
}; };

@ -6,11 +6,19 @@ const createRegexParser = require("../../text-parser-regex");
const errors = require("../errors"); const errors = require("../errors");
module.exports = function (devicePath) { module.exports = function (devicePath) {
return function handleDeviceNotFound(command) { function makeError() {
return command.expectOnStderr(createRegexParser(/Device .+ not found\./, () => {
return errorResult(new errors.InvalidPath(`Specified device '${devicePath}' does not exist`, { return errorResult(new errors.InvalidPath(`Specified device '${devicePath}' does not exist`, {
path: devicePath path: devicePath
})); }));
}
return function handleDeviceNotFound(command) {
return command
.expectOnStderr(createRegexParser(/Device .+ not found\./, () => {
return makeError();
}))
.expectOnStderr(createRegexParser(/No device found for .+\./, () => {
return makeError();
})); }));
}; };
}; };

@ -0,0 +1,17 @@
"use strict";
const { errorResult } = require("../../text-parser");
const createRegexParser = require("../../text-parser-regex");
const errors = require("../errors");
module.exports = function (devicePath) {
return function handleDeviceTooSmall(command) {
return command
.expectOnStderr(createRegexParser(/Cannot use .+: device is too small \(pv_min_size\)/, () => {
return errorResult(new errors.DeviceTooSmall(`Specified device '${devicePath}' is too small to create a physical volume on`, {
path: devicePath
}));
}));
};
};

@ -80,8 +80,9 @@ module.exports = {
// mtfa, // mtfa,
// hmpre, // hmpre,
// hmmin, // hmmin,
tnvmcap: { name: "totalSpace", transform: (value) => B(value) }, // FIXME: The following two values are exported as string (in newer versions of nvme-cli), presumably for precision reasons. Eventually the units package should probably be updated to support bigints, to avoid precision loss in our own code.
unvmcap: { name: "freeSpace", transform: (value) => B(value) }, tnvmcap: { name: "totalSpace", transform: (value) => B(parseInt(value)) },
unvmcap: { name: "freeSpace", transform: (value) => B(parseInt(value)) },
// TOOD: // TOOD:
// rpmbs, // rpmbs,
// edstt, // edstt,

@ -1,7 +1,6 @@
"use strict"; "use strict";
/* TODO: /* TODO:
toDisplay
conversion between unit scales (eg. IEC -> metric bytes) conversion between unit scales (eg. IEC -> metric bytes)
ensure NaN is handled correctly ensure NaN is handled correctly
Track the originally-constructed value internally, so that stacked conversions can be done losslessly? Track the originally-constructed value internally, so that stacked conversions can be done losslessly?

@ -1,7 +1,5 @@
"use strict"; "use strict";
const assert = require("assert");
function createResultObject(isSuccessful, containedValue) { function createResultObject(isSuccessful, containedValue) {
return { return {
__isResultType: true, __isResultType: true,

@ -0,0 +1,64 @@
"use strict";
module.exports = {
name: "LVM",
initialize: function () {
let types = {
"sysquery.lvm.PhysicalVolume": function PhysicalVolume({ path }) {
},
"sysquery.lvm.VolumeGroup": function VolumeGroup({ name }) {
},
"sysquery.lvm.LogicalVolume": function LogicalVolume({ path }) {
}
};
return {
sources: () => {
return {
physicalVolumes: ,
volumeGroups: ,
logicalVolumes:
};
},
types: types,
extensions: {
"sysquery.core.BlockDevice": {
lvmPhysicalVolume: function (_, { $getProperty, sources }) {
}
}
},
root: {
resources: {
lvm: {
physicalVolumes: ({ paths }, { sources }) => {
return makeTypeFromSource({
source: sources.physicalVolumes,
ids: paths,
make: (result) => types.PhysicalVolume({ path: result.path }),
});
},
volumeGroups: ({ names }, { sources }) => {
return makeTypeFromSource({
source: sources.volumeGroups,
ids: names,
make: (result) => types.VolumeGroup({ name: result.name }),
});
},
logicalVolumes: ({ paths }, { sources }) => {
// FIXME: Aren't these scoped to a volume group?
return makeTypeFromSource({
source: sources.logicalVolumes,
ids: paths,
make: (result) => types.LogicalVolume({ path: result.path }),
});
}
}
}
}
};
}
};

@ -189,3 +189,31 @@ table.drives {
color: gray; color: gray;
} }
} }
.volumeGroup {
max-width: 960px;
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
gap: .2em .4em;
}
.bar {
border: 1px solid black;
display: grid;
}
.barSegment {
box-sizing: border-box;
font-size: .8em;
background-color: rgb(205, 205, 205);
padding: .3em .4em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:nth-child(even) {
background-color: rgb(177, 177, 177);
}
}

@ -18,7 +18,9 @@ module.exports = {
format: true, format: true,
totalSpace: true, totalSpace: true,
freeSpace: true, freeSpace: true,
status: true isAllocatable: true,
isUsed: true,
isDuplicate: true,
}, },
logicalVolumes: { logicalVolumes: {
name: true, name: true,
@ -35,8 +37,59 @@ module.exports = {
} }
}, },
template: function ({ data }) { template: function ({ data }) {
function SegmentBar({ items }) {
let columns = items
.map((item) => `${item.value}fr`)
.join(" ");
return (
<div className="bar" style={{ gridTemplateColumns: columns }}>
{items.map((item) => {
return (
<div key={item.key} className="barSegment" title={item.label}>{item.label}</div>
);
})}
</div>
);
}
return ( return (
<Layout title="LVM"> <Layout title="LVM">
{data.resources.lvm.volumeGroups.map((volumeGroup) => {
return (<>
<h2>{volumeGroup.name}</h2>
<div className="volumeGroup">
<div className="label">Physical volumes:</div>
<SegmentBar
items={volumeGroup.physicalVolumes.map((physicalVolume) => {
return {
key: physicalVolume.path,
value: Math.round(physicalVolume.totalSpace.toMiB().amount),
label: `${physicalVolume.path} (${physicalVolume.totalSpace.toDisplay(2)})`
};
})}
/>
<div className="label">Volume group:</div>
<SegmentBar
items={[{
key: volumeGroup.name,
value: 1,
label: `${volumeGroup.name} (${volumeGroup.totalSpace.toDisplay(2)})`
}]}
/>
<div className="label">Logical volumes:</div>
<SegmentBar
items={volumeGroup.logicalVolumes.map((logicalVolume) => {
return {
key: logicalVolume.name,
value: Math.round(logicalVolume.size.toMiB().amount),
label: `${logicalVolume.name} (${logicalVolume.size.toDisplay(2)})`
};
})}
/>
</div>
</>);
})}
<DebugView value={data} /> <DebugView value={data} />
</Layout> </Layout>
); );

@ -147,6 +147,8 @@ module.exports = {
let totalFailingStorage = sumDriveSizes(drivesByStatus.FAILING); let totalFailingStorage = sumDriveSizes(drivesByStatus.FAILING);
let totalUnknownStorage = sumDriveSizes(drivesByStatus.UNKNOWN); let totalUnknownStorage = sumDriveSizes(drivesByStatus.UNKNOWN);
// TODO: Show unallocated space
return ( return (
<Layout title="Storage Devices"> <Layout title="Storage Devices">
<table className="drives"> <table className="drives">

@ -2,6 +2,7 @@
require("@babel/register"); require("@babel/register");
const Promise = require("bluebird"); const Promise = require("bluebird");
const errorChain = require("error-chain");
const createAPI = require("../src/api"); const createAPI = require("../src/api");
// const query = { // const query = {
@ -41,23 +42,13 @@ const createAPI = require("../src/api");
const query = { const query = {
resources: { resources: {
lvm: { lvm: {
physicalVolumes: { createPhysicalVolume: {
$arguments: {
path: "/dev/loop3"
},
path: true, path: true,
totalSpace: true, totalSpace: true,
freeSpace: true, freeSpace: true
status: true,
volumeGroup: {
name: true,
totalSpace: true,
freeSpace: true,
logicalVolumeCount: true,
mode: true,
physicalVolumes: {
path: true
}
}
} }
} }
} }
@ -69,4 +60,6 @@ return Promise.try(() => {
return api.query(query); return api.query(query);
}).then((result) => { }).then((result) => {
console.dir(result, { depth: null }); console.dir(result, { depth: null });
}).catch((error) => {
console.error(errorChain.render(error));
}); });

@ -0,0 +1,2 @@
sudo losetup -P /dev/loop1 vdisk1
sudo losetup -P /dev/loop2 vdisk2

@ -0,0 +1,2 @@
fallocate -l 5G vdisk1
fallocate -l 5G vdisk2

@ -938,6 +938,11 @@
lodash.escaperegexp "^4.1.2" lodash.escaperegexp "^4.1.2"
object-assign "^4.1.1" object-assign "^4.1.1"
"@joepie91/result@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@joepie91/result/-/result-0.1.0.tgz#187b97033edf200698ad159ea0edc907fce2cad0"
integrity sha512-2qjcinMrUV1FSA4g5AG6t32ijGTmcUzY5XIFJoNP0zQYtlM/C2NaLDcFHtwgASTMW0p3ZIkgueGlvwQe0S7Kxg==
"@joepie91/unreachable@^1.0.0": "@joepie91/unreachable@^1.0.0":
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@joepie91/unreachable/-/unreachable-1.0.0.tgz#8032bb8a5813e81bbbe516cb3031d60818526687" resolved "https://registry.yarnpkg.com/@joepie91/unreachable/-/unreachable-1.0.0.tgz#8032bb8a5813e81bbbe516cb3031d60818526687"
@ -1037,6 +1042,32 @@
supports-color "^7.1.0" supports-color "^7.1.0"
syncpipe "^1.0.0" syncpipe "^1.0.0"
"@validatem/core@^0.3.3":
version "0.3.17"
resolved "https://registry.yarnpkg.com/@validatem/core/-/core-0.3.17.tgz#1756a7eca0523a3657794d2060273f7d42c083ef"
integrity sha512-VahE9TAKpaU13BcVQI/Dc9j/xsm/BgloRM0v1HjOMpoJ16tOkKQkUdOgiDCG4zmEek1bG3v9Zu4lS1lubgjLMw==
dependencies:
"@validatem/annotate-errors" "^0.1.2"
"@validatem/any-property" "^0.1.0"
"@validatem/error" "^1.0.0"
"@validatem/match-validation-error" "^0.1.0"
"@validatem/match-versioned-special" "^0.1.0"
"@validatem/match-virtual-property" "^0.1.0"
"@validatem/normalize-rules" "^0.1.0"
"@validatem/required" "^0.1.0"
"@validatem/validation-result" "^0.1.1"
"@validatem/virtual-property" "^0.1.0"
as-expression "^1.0.0"
assure-array "^1.0.0"
create-error "^0.3.1"
default-value "^1.0.0"
execall "^2.0.0"
flatten "^1.0.3"
indent-string "^4.0.0"
is-arguments "^1.0.4"
supports-color "^7.1.0"
syncpipe "^1.0.0"
"@validatem/default-to@^0.1.0": "@validatem/default-to@^0.1.0":
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/@validatem/default-to/-/default-to-0.1.0.tgz#62766a3ca24d2f61a96c713bcb629a5b3c6427c5" resolved "https://registry.yarnpkg.com/@validatem/default-to/-/default-to-0.1.0.tgz#62766a3ca24d2f61a96c713bcb629a5b3c6427c5"
@ -1194,6 +1225,11 @@
dependencies: dependencies:
"@validatem/error" "^1.0.0" "@validatem/error" "^1.0.0"
"@validatem/remove-nullish-items@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@validatem/remove-nullish-items/-/remove-nullish-items-0.1.0.tgz#fe1a8b64d11276b506fae2bd2c41da4985a5b5ff"
integrity sha512-cs4YSF47TA/gHnV5muSUUqGi5PwybP5ztu5SYnPKxQVTyubvcbrFat51nOvJ2PmUasyrIccoYMmATiviXkTi6g==
"@validatem/require-either@^0.1.0": "@validatem/require-either@^0.1.0":
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/@validatem/require-either/-/require-either-0.1.0.tgz#250e35ab06f124ea90f3925d74b5f53a083923b0" resolved "https://registry.yarnpkg.com/@validatem/require-either/-/require-either-0.1.0.tgz#250e35ab06f124ea90f3925d74b5f53a083923b0"
@ -1252,6 +1288,15 @@
default-value "^1.0.0" default-value "^1.0.0"
split-filter-n "^1.1.2" split-filter-n "^1.1.2"
"@validatem/wrap-path@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@validatem/wrap-path/-/wrap-path-0.1.0.tgz#777998b62d3e74f2b2897c992dae9b3675161c33"
integrity sha512-6hOqydnr4u8FA0iRv8fyXxsr64T99+w/XL/fixmsgN0uqulEIwGMxCre3y9YkFNcEtysyPHkQl0CrGPcASsZxw==
dependencies:
"@validatem/annotate-errors" "^0.1.2"
"@validatem/combinator" "^0.1.2"
"@validatem/validation-result" "^0.1.2"
JSONStream@^1.0.3, JSONStream@^1.1.4: JSONStream@^1.0.3, JSONStream@^1.1.4:
version "1.3.5" version "1.3.5"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
@ -2859,6 +2904,16 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0" miller-rabin "^4.0.0"
randombytes "^2.0.0" randombytes "^2.0.0"
dlayer@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dlayer/-/dlayer-0.1.0.tgz#cef49d283e1a9d83a8bd69a4c01d468ddd0b898c"
integrity sha512-UrdQdihmoNbWv49YFD/MBspaS2ILEfrNBoIVvZb1fhwvnPoRQ3ZOAbyiyV1sdfTrCASWVMz4i2NFczOVoVWJxw==
dependencies:
"@joepie91/result" "^0.1.0"
bluebird "^3.4.6"
map-obj "^4.2.1"
syncpipe "^1.0.0"
dns-packet@^5.1.2: dns-packet@^5.1.2:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.2.1.tgz#26cec0be92252a1b97ed106482921192a7e08f72" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.2.1.tgz#26cec0be92252a1b97ed106482921192a7e08f72"
@ -5157,6 +5212,23 @@ memoizee@^0.4.14:
next-tick "^1.1.0" next-tick "^1.1.0"
timers-ext "^0.1.7" timers-ext "^0.1.7"
merge-by-template@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/merge-by-template/-/merge-by-template-0.1.4.tgz#8a03e6383a4e2f2e4a6460bff0d6d3e7b468a535"
integrity sha512-10h5HyGLJJu1F1z02oMqpvMa6oraLr7Vp0gPxlw6Od8xlvzTFr0TQGPZXMLBmZlhZRY910AXGJ6AFc2iXGZ7uQ==
dependencies:
"@validatem/core" "^0.3.3"
"@validatem/default-to" "^0.1.0"
"@validatem/is-array" "^0.1.1"
"@validatem/is-boolean" "^0.1.1"
"@validatem/is-plain-object" "^0.1.1"
"@validatem/remove-nullish-items" "^0.1.0"
"@validatem/virtual-property" "^0.1.0"
"@validatem/wrap-path" "^0.1.0"
default-value "^1.0.0"
fromentries "^1.2.0"
range "^0.0.3"
merge-descriptors@1.0.1: merge-descriptors@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@ -6346,6 +6418,11 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
range@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/range/-/range-0.0.3.tgz#b5b8eb2463a516b624a563bd32b18fe89e70151b"
integrity sha512-OxK2nY2bmeEB4NxoBraQIBOOeOIxoBvm6yt8MA1kLappgkG3SyLf173iOtT5woWycrtESDD2g0Nl2yt8YPoUnw==
raw-body@2.4.0: raw-body@2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"

Loading…
Cancel
Save