Skip to content

Commit

Permalink
experimental features for zram and reset tor (#2299)
Browse files Browse the repository at this point in the history
* experimental features for zram and reset tor

* zram backend

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
  • Loading branch information
MattDHill and dr-bonez authored Jun 9, 2023
1 parent f9d401e commit f20f695
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 1 deletion.
3 changes: 3 additions & 0 deletions backend/src/db/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ impl Database {
.map(|x| format!("{x:X}"))
.join(":"),
system_start_time: Utc::now().to_rfc3339(),
zram: false,
},
package_data: AllPackageData::default(),
ui: serde_json::from_str(include_str!("../../../frontend/patchdb-ui-seed.json"))
Expand Down Expand Up @@ -117,6 +118,8 @@ pub struct ServerInfo {
pub pubkey: String,
pub ca_fingerprint: String,
pub system_start_time: String,
#[serde(default)]
pub zram: bool,
}

#[derive(Debug, Deserialize, Serialize, HasModel)]
Expand Down
1 change: 1 addition & 0 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub fn main_api() -> Result<(), RpcError> {

#[command(subcommands(
system::time,
system::experimental,
system::logs,
system::kernel_logs,
system::metrics,
Expand Down
55 changes: 54 additions & 1 deletion backend/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,59 @@ use crate::{Error, ErrorKind, ResultExt};

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]
pub async fn time() -> Result<String, Error> {
Ok(Utc::now().to_rfc3339())
Expand Down Expand Up @@ -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_free = MebiBytes(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 {
percentage_used,
total,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const ICONS = [
'file-tray-stacked-outline',
'finger-print-outline',
'flash-outline',
'flash-off-outline',
'folder-open-outline',
'globe-outline',
'grid-outline',
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {}
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ const routes: Routes = [
loadChildren: () =>
import('./wifi/wifi.module').then(m => m.WifiPageModule),
},
{
path: 'experimental-features',
loadChildren: () =>
import('./experimental-features/experimental-features.module').then(
m => m.ExperimentalFeaturesPageModule,
),
},
]

@NgModule({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,17 @@ export class ServerShowPage {
detail: true,
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: [
{
Expand Down
5 changes: 5 additions & 0 deletions frontend/projects/ui/src/app/services/api/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ export module RR {
} // net.tor.reset
export type ResetTorRes = null

export type ToggleZramReq = {
enable: boolean
} // server.experimental.zram
export type ToggleZramRes = null

// sessions

export type GetSessionsReq = {} // sessions.list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export abstract class ApiService {

abstract resetTor(params: RR.ResetTorReq): Promise<RR.ResetTorRes>

abstract toggleZram(params: RR.ToggleZramReq): Promise<RR.ToggleZramRes>

// marketplace URLs

abstract marketplaceProxy<T>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ export class LiveApiService extends ApiService {
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

async marketplaceProxy<T>(
Expand Down
Loading

0 comments on commit f20f695

Please sign in to comment.