mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
experimental features for zram and reset tor (#2299)
* experimental features for zram and reset tor * zram backend --------- Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -80,6 +80,7 @@ impl Database {
|
|||||||
.map(|x| format!("{x:X}"))
|
.map(|x| format!("{x:X}"))
|
||||||
.join(":"),
|
.join(":"),
|
||||||
system_start_time: Utc::now().to_rfc3339(),
|
system_start_time: Utc::now().to_rfc3339(),
|
||||||
|
zram: false,
|
||||||
},
|
},
|
||||||
package_data: AllPackageData::default(),
|
package_data: AllPackageData::default(),
|
||||||
ui: serde_json::from_str(include_str!("../../../frontend/patchdb-ui-seed.json"))
|
ui: serde_json::from_str(include_str!("../../../frontend/patchdb-ui-seed.json"))
|
||||||
@@ -117,6 +118,8 @@ pub struct ServerInfo {
|
|||||||
pub pubkey: String,
|
pub pubkey: String,
|
||||||
pub ca_fingerprint: String,
|
pub ca_fingerprint: String,
|
||||||
pub system_start_time: String,
|
pub system_start_time: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub zram: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ pub fn main_api() -> Result<(), RpcError> {
|
|||||||
|
|
||||||
#[command(subcommands(
|
#[command(subcommands(
|
||||||
system::time,
|
system::time,
|
||||||
|
system::experimental,
|
||||||
system::logs,
|
system::logs,
|
||||||
system::kernel_logs,
|
system::kernel_logs,
|
||||||
system::metrics,
|
system::metrics,
|
||||||
|
|||||||
@@ -24,6 +24,59 @@ use crate::{Error, ErrorKind, ResultExt};
|
|||||||
|
|
||||||
pub const SYSTEMD_UNIT: &'static str = "embassyd";
|
pub const SYSTEMD_UNIT: &'static str = "embassyd";
|
||||||
|
|
||||||
|
#[command(subcommands(zram))]
|
||||||
|
pub async fn experimental() -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command(display(display_none))]
|
||||||
|
pub async fn zram(#[context] ctx: RpcContext, #[arg] enable: bool) -> Result<(), Error> {
|
||||||
|
let mut db = ctx.db.handle();
|
||||||
|
let zram = crate::db::DatabaseModel::new()
|
||||||
|
.server_info()
|
||||||
|
.zram()
|
||||||
|
.get_mut(&mut db)
|
||||||
|
.await?;
|
||||||
|
if enable == *zram {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if enable {
|
||||||
|
let mem_info = get_mem_info().await?;
|
||||||
|
Command::new("modprobe")
|
||||||
|
.arg("zram")
|
||||||
|
.invoke(ErrorKind::Zram)
|
||||||
|
.await?;
|
||||||
|
tokio::fs::write("/sys/block/zram0/comp_algorithm", "lz4")
|
||||||
|
.await
|
||||||
|
.with_kind(ErrorKind::Zram)?;
|
||||||
|
tokio::fs::write(
|
||||||
|
"/sys/block/zram0/disksize",
|
||||||
|
format!("{}M", mem_info.total.0 as u64 / 4),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_kind(ErrorKind::Zram)?;
|
||||||
|
Command::new("mkswap")
|
||||||
|
.arg("/dev/zram0")
|
||||||
|
.invoke(ErrorKind::Zram)
|
||||||
|
.await?;
|
||||||
|
Command::new("swapon")
|
||||||
|
.arg("-p")
|
||||||
|
.arg("5")
|
||||||
|
.arg("/dev/zram0")
|
||||||
|
.invoke(ErrorKind::Zram)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
Command::new("swapoff")
|
||||||
|
.arg("/dev/zram0")
|
||||||
|
.invoke(ErrorKind::Zram)
|
||||||
|
.await?;
|
||||||
|
tokio::fs::write("/sys/block/zram0/reset", "1")
|
||||||
|
.await
|
||||||
|
.with_kind(ErrorKind::Zram)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn time() -> Result<String, Error> {
|
pub async fn time() -> Result<String, Error> {
|
||||||
Ok(Utc::now().to_rfc3339())
|
Ok(Utc::now().to_rfc3339())
|
||||||
@@ -699,7 +752,7 @@ async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
|||||||
let swap_total = MebiBytes(swap_total_k as f64 / 1024.0);
|
let swap_total = MebiBytes(swap_total_k as f64 / 1024.0);
|
||||||
let swap_free = MebiBytes(swap_free_k as f64 / 1024.0);
|
let swap_free = MebiBytes(swap_free_k as f64 / 1024.0);
|
||||||
let swap_used = MebiBytes((swap_total_k - swap_free_k) as f64 / 1024.0);
|
let swap_used = MebiBytes((swap_total_k - swap_free_k) as f64 / 1024.0);
|
||||||
let percentage_used = Percentage(used.0 / total.0 * 100.0);
|
let percentage_used = Percentage((total.0 - available.0) / total.0 * 100.0);
|
||||||
Ok(MetricsMemory {
|
Ok(MetricsMemory {
|
||||||
percentage_used,
|
percentage_used,
|
||||||
total,
|
total,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const ICONS = [
|
|||||||
'file-tray-stacked-outline',
|
'file-tray-stacked-outline',
|
||||||
'finger-print-outline',
|
'finger-print-outline',
|
||||||
'flash-outline',
|
'flash-outline',
|
||||||
|
'flash-off-outline',
|
||||||
'folder-open-outline',
|
'folder-open-outline',
|
||||||
'globe-outline',
|
'globe-outline',
|
||||||
'grid-outline',
|
'grid-outline',
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { Routes, RouterModule } from '@angular/router'
|
||||||
|
import { IonicModule } from '@ionic/angular'
|
||||||
|
import { ExperimentalFeaturesPage } from './experimental-features.page'
|
||||||
|
import { EmverPipesModule } from '@start9labs/shared'
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: ExperimentalFeaturesPage,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
EmverPipesModule,
|
||||||
|
],
|
||||||
|
declarations: [ExperimentalFeaturesPage],
|
||||||
|
})
|
||||||
|
export class ExperimentalFeaturesPageModule {}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button defaultHref="system"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>Experimental Features</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content class="with-widgets">
|
||||||
|
<ion-item-group *ngIf="server$ | async as server">
|
||||||
|
<ion-item button (click)="presentAlertResetTor()">
|
||||||
|
<ion-icon slot="start" name="reload"></ion-icon>
|
||||||
|
<ion-label>
|
||||||
|
<h2>Reset Tor</h2>
|
||||||
|
<p>
|
||||||
|
Reset the Tor deamon on your server may resolve Tor connectivity
|
||||||
|
issues.
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item button (click)="presentAlertZram(server.zram)">
|
||||||
|
<ion-icon
|
||||||
|
slot="start"
|
||||||
|
[name]="server.zram ? 'flash-off-outline' : 'flash-outline'"
|
||||||
|
></ion-icon>
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ server.zram ? 'Disable' : 'Enable' }} zram</h2>
|
||||||
|
<p>
|
||||||
|
Enabling zram may improve server performance, especially on low RAM
|
||||||
|
devices
|
||||||
|
</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-item-group>
|
||||||
|
</ion-content>
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||||
|
import {
|
||||||
|
AlertController,
|
||||||
|
LoadingController,
|
||||||
|
ToastController,
|
||||||
|
} from '@ionic/angular'
|
||||||
|
import { PatchDB } from 'patch-db-client'
|
||||||
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
|
import { ErrorToastService } from '@start9labs/shared'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'experimental-features',
|
||||||
|
templateUrl: './experimental-features.page.html',
|
||||||
|
styleUrls: ['./experimental-features.page.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ExperimentalFeaturesPage {
|
||||||
|
readonly server$ = this.patch.watch$('server-info')
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly toastCtrl: ToastController,
|
||||||
|
private readonly patch: PatchDB<DataModel>,
|
||||||
|
private readonly config: ConfigService,
|
||||||
|
private readonly alertCtrl: AlertController,
|
||||||
|
private readonly loadingCtrl: LoadingController,
|
||||||
|
private readonly api: ApiService,
|
||||||
|
private readonly errToast: ErrorToastService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async presentAlertResetTor() {
|
||||||
|
const isTor = this.config.isTor()
|
||||||
|
const shared =
|
||||||
|
'Optionally wipe state to forcibly acquire new guard nodes. It is recommended to try without wiping state first.'
|
||||||
|
const alert = await this.alertCtrl.create({
|
||||||
|
header: isTor ? 'Warning' : 'Confirm',
|
||||||
|
message: isTor
|
||||||
|
? `You are currently connected over Tor. If you reset the Tor daemon, you will loose connectivity until it comes back online.<br/><br/>${shared}`
|
||||||
|
: `Reset Tor?<br/><br/>${shared}`,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
label: 'Wipe state',
|
||||||
|
type: 'checkbox',
|
||||||
|
value: 'wipe',
|
||||||
|
handler: val => {
|
||||||
|
console.error(val)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Reset',
|
||||||
|
handler: (value: string[]) => {
|
||||||
|
console.error(value)
|
||||||
|
this.resetTor(value.some(v => 'wipe'))
|
||||||
|
},
|
||||||
|
cssClass: 'enter-click',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cssClass: isTor ? 'alert-warning-message' : '',
|
||||||
|
})
|
||||||
|
await alert.present()
|
||||||
|
}
|
||||||
|
|
||||||
|
async presentAlertZram(enabled: boolean) {
|
||||||
|
const alert = await this.alertCtrl.create({
|
||||||
|
header: enabled ? 'Confirm' : 'Warning',
|
||||||
|
message: enabled
|
||||||
|
? 'Are you sure you want to disable zram?'
|
||||||
|
: 'zram on StartOS is experimental. It may increase performance of you server, especially if it is a low RAM device.',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: enabled ? 'Disable' : 'Enable',
|
||||||
|
handler: () => {
|
||||||
|
this.toggleZram(enabled)
|
||||||
|
},
|
||||||
|
cssClass: 'enter-click',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cssClass: enabled ? '' : 'alert-warning-message',
|
||||||
|
})
|
||||||
|
await alert.present()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resetTor(wipeState: boolean) {
|
||||||
|
const loader = await this.loadingCtrl.create({
|
||||||
|
message: 'Resetting Tor...',
|
||||||
|
})
|
||||||
|
await loader.present()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.resetTor({
|
||||||
|
'wipe-state': wipeState,
|
||||||
|
reason: 'User triggered',
|
||||||
|
})
|
||||||
|
const toast = await this.toastCtrl.create({
|
||||||
|
header: 'Tor reset in progress',
|
||||||
|
position: 'bottom',
|
||||||
|
duration: 4000,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
side: 'start',
|
||||||
|
icon: 'close',
|
||||||
|
handler: () => {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await toast.present()
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errToast.present(e)
|
||||||
|
} finally {
|
||||||
|
loader.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async toggleZram(enabled: boolean) {
|
||||||
|
const loader = await this.loadingCtrl.create({
|
||||||
|
message: enabled ? 'Disabling zram...' : 'Enabling zram',
|
||||||
|
})
|
||||||
|
await loader.present()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.toggleZram({ enable: !enabled })
|
||||||
|
const toast = await this.toastCtrl.create({
|
||||||
|
header: `Zram ${enabled ? 'disabled' : 'enabled'}`,
|
||||||
|
position: 'bottom',
|
||||||
|
duration: 4000,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
side: 'start',
|
||||||
|
icon: 'close',
|
||||||
|
handler: () => {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await toast.present()
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errToast.present(e)
|
||||||
|
} finally {
|
||||||
|
loader.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,6 +80,13 @@ const routes: Routes = [
|
|||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('./wifi/wifi.module').then(m => m.WifiPageModule),
|
import('./wifi/wifi.module').then(m => m.WifiPageModule),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'experimental-features',
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./experimental-features/experimental-features.module').then(
|
||||||
|
m => m.ExperimentalFeaturesPageModule,
|
||||||
|
),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|||||||
@@ -436,6 +436,17 @@ export class ServerShowPage {
|
|||||||
detail: true,
|
detail: true,
|
||||||
disabled$: of(false),
|
disabled$: of(false),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Experimental Features',
|
||||||
|
description: 'Try out new and potentially unstable new features',
|
||||||
|
icon: 'flask-outline',
|
||||||
|
action: () =>
|
||||||
|
this.navCtrl.navigateForward(['experimental-features'], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
}),
|
||||||
|
detail: true,
|
||||||
|
disabled$: of(false),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
Insights: [
|
Insights: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ export module RR {
|
|||||||
} // net.tor.reset
|
} // net.tor.reset
|
||||||
export type ResetTorRes = null
|
export type ResetTorRes = null
|
||||||
|
|
||||||
|
export type ToggleZramReq = {
|
||||||
|
enable: boolean
|
||||||
|
} // server.experimental.zram
|
||||||
|
export type ToggleZramRes = null
|
||||||
|
|
||||||
// sessions
|
// sessions
|
||||||
|
|
||||||
export type GetSessionsReq = {} // sessions.list
|
export type GetSessionsReq = {} // sessions.list
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ export abstract class ApiService {
|
|||||||
|
|
||||||
abstract resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes>
|
abstract resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes>
|
||||||
|
|
||||||
|
abstract toggleZram(params: RR.ToggleZramReq): Promise<RR.ToggleZramRes>
|
||||||
|
|
||||||
// marketplace URLs
|
// marketplace URLs
|
||||||
|
|
||||||
abstract marketplaceProxy<T>(
|
abstract marketplaceProxy<T>(
|
||||||
|
|||||||
@@ -196,6 +196,10 @@ export class LiveApiService extends ApiService {
|
|||||||
return this.rpcRequest({ method: 'net.tor.reset', params })
|
return this.rpcRequest({ method: 'net.tor.reset', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async toggleZram(params: RR.ToggleZramReq): Promise<RR.ToggleZramRes> {
|
||||||
|
return this.rpcRequest({ method: 'server.experimental.zram', params })
|
||||||
|
}
|
||||||
|
|
||||||
// marketplace URLs
|
// marketplace URLs
|
||||||
|
|
||||||
async marketplaceProxy<T>(
|
async marketplaceProxy<T>(
|
||||||
|
|||||||
@@ -327,6 +327,18 @@ export class MockApiService extends ApiService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async toggleZram(params: RR.ToggleZramReq): Promise<RR.ToggleZramRes> {
|
||||||
|
await pauseFor(2000)
|
||||||
|
const patch = [
|
||||||
|
{
|
||||||
|
op: PatchOp.REPLACE,
|
||||||
|
path: '/server-info/zram',
|
||||||
|
value: params.enable,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return this.withRevision(patch, null)
|
||||||
|
}
|
||||||
|
|
||||||
// marketplace URLs
|
// marketplace URLs
|
||||||
|
|
||||||
async marketplaceProxy(
|
async marketplaceProxy(
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export const mockPatchData: DataModel = {
|
|||||||
pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m',
|
pubkey: 'npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m',
|
||||||
'ca-fingerprint': 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15',
|
'ca-fingerprint': 'SHA-256: 63 2B 11 99 44 40 17 DF 37 FC C3 DF 0F 3D 15',
|
||||||
'system-start-time': new Date(new Date().valueOf() - 360042).toUTCString(),
|
'system-start-time': new Date(new Date().valueOf() - 360042).toUTCString(),
|
||||||
|
zram: false,
|
||||||
},
|
},
|
||||||
'package-data': {
|
'package-data': {
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ export interface ServerInfo {
|
|||||||
pubkey: string
|
pubkey: string
|
||||||
'ca-fingerprint': string
|
'ca-fingerprint': string
|
||||||
'system-start-time': string
|
'system-start-time': string
|
||||||
|
zram: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IpInfo {
|
export interface IpInfo {
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ pub enum ErrorKind {
|
|||||||
Grub = 64,
|
Grub = 64,
|
||||||
Systemd = 65,
|
Systemd = 65,
|
||||||
OpenSsh = 66,
|
OpenSsh = 66,
|
||||||
|
Zram = 67,
|
||||||
}
|
}
|
||||||
impl ErrorKind {
|
impl ErrorKind {
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
@@ -146,6 +147,7 @@ impl ErrorKind {
|
|||||||
Grub => "Grub Error",
|
Grub => "Grub Error",
|
||||||
Systemd => "Systemd Error",
|
Systemd => "Systemd Error",
|
||||||
OpenSsh => "OpenSSH Error",
|
OpenSsh => "OpenSSH Error",
|
||||||
|
Zram => "Zram Error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user