Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-131326 / 25.04 / Simplify HA upgrade logic + NAS-131325: system.reboot.info and failover.reboot.info methods and events #10744

Merged
merged 26 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a31ad6e
NAS-131326: Simplify HA upgrade logic
bvasilenko Sep 24, 2024
544ef1c
NAS-131326: Add Reboot Info store
bvasilenko Sep 24, 2024
a7cf359
NAS-131326: Add Reboot Info dialog
bvasilenko Sep 25, 2024
cb566cb
NAS-131326: Add unit tests
bvasilenko Sep 26, 2024
cb9f196
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Sep 27, 2024
424ddbe
NAS-131326: Add unit tests
bvasilenko Sep 30, 2024
3918657
NAS-131326: Fix remarks
bvasilenko Oct 3, 2024
80a2d08
NAS-131326: Remove unnecessary filters
bvasilenko Oct 7, 2024
14193eb
NAS-131326: Rename `RebootDialog` to `RebootRequiredDialog`
bvasilenko Oct 7, 2024
dbca167
NAS-131326: Add reboot reason
bvasilenko Oct 9, 2024
21ad32c
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Oct 9, 2024
f92048d
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Oct 10, 2024
261ae44
NAS-131326: Fix merge errors
bvasilenko Oct 14, 2024
1b0b76f
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Oct 15, 2024
e68f634
NAS-131326: Add cancel button
bvasilenko Oct 15, 2024
a590d42
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Oct 17, 2024
f914985
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Oct 30, 2024
aa95da5
NAS-131326: Fix linter errors
bvasilenko Oct 30, 2024
68d3419
NAS-131326: Remove `HaFipsEffects`
bvasilenko Oct 31, 2024
dff1d06
NAS-131326: Fix remarks
bvasilenko Nov 4, 2024
ef34519
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Nov 5, 2024
a84c004
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Nov 12, 2024
185f0b0
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Nov 14, 2024
7af5895
Merge branch 'origin/master' into 131326/NAS-131326
bvasilenko Nov 14, 2024
65644a4
Merge branch 'master' into NAS-131326
bvasilenko Nov 15, 2024
506bccb
Merge branch 'master' into NAS-131326
bvasilenko Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/helptext/topbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const helptextTopbar = {
tc_connect: T('Connecting to TrueCommand'),
tc_status: T('Status of TrueCommand'),
update: T('Update in Progress'),
upgrade_waiting: T('Upgrade Waiting to Finish'),
reboot_info: T('Reboot Required'),
pending_network_changes: T('Pending Network Changes'),
directory_services_monitor: T('Directory Services Monitor'),
resilvering: T('Resilvering'),
Expand Down
4 changes: 3 additions & 1 deletion src/app/interfaces/api/api-call-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ import {
import { Privilege, PrivilegeRole, PrivilegeUpdate } from 'app/interfaces/privilege.interface';
import { Process } from 'app/interfaces/process.interface';
import { QueryParams } from 'app/interfaces/query-api.interface';
import { FailoverRebootInfo, SystemRebootInfo } from 'app/interfaces/reboot-info.interface';
import { ReplicationConfigUpdate } from 'app/interfaces/replication-config-update.interface';
import { ReplicationConfig } from 'app/interfaces/replication-config.interface';
import {
Expand Down Expand Up @@ -460,11 +461,11 @@ export interface ApiCallDirectory {
'failover.get_ips': { params: void; response: string[] };
'failover.licensed': { params: void; response: boolean };
'failover.node': { params: void; response: string };
'failover.reboot.info': { params: void; response: FailoverRebootInfo };
'failover.status': { params: void; response: FailoverStatus };
'failover.sync_from_peer': { params: void; response: void };
'failover.sync_to_peer': { params: [{ reboot?: boolean }]; response: void };
'failover.update': { params: [FailoverUpdate]; response: FailoverConfig };
'failover.upgrade_pending': { params: void; response: boolean };

// Filesystem
'filesystem.acl_is_trivial': {
Expand Down Expand Up @@ -815,6 +816,7 @@ export interface ApiCallDirectory {
'system.security.info.fips_available': { params: void; response: boolean };
'system.set_time': { params: [number]; response: void };
'system.ready': { params: void; response: boolean };
'system.reboot.info': { params: void; response: SystemRebootInfo };
'system.state': { params: void; response: SystemState };
'system.version': { params: void; response: string };
'system.version_short': { params: void; response: string };
Expand Down
3 changes: 3 additions & 0 deletions src/app/interfaces/api/api-event-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FailoverDisabledReasonEvent } from 'app/interfaces/failover-disabled-re
import { Group } from 'app/interfaces/group.interface';
import { Job } from 'app/interfaces/job.interface';
import { Pool } from 'app/interfaces/pool.interface';
import { FailoverRebootInfo, SystemRebootInfo } from 'app/interfaces/reboot-info.interface';
import { ReportingRealtimeUpdate } from 'app/interfaces/reporting.interface';
import { PoolScan } from 'app/interfaces/resilver-job.interface';
import { Service } from 'app/interfaces/service.interface';
Expand All @@ -28,12 +29,14 @@ export interface ApiEventDirectory {
'disk.query': { response: Disk };
'docker.state': { response: DockerStatusData };
'failover.disabled.reasons': { response: FailoverDisabledReasonEvent };
'failover.reboot.info': { response: FailoverRebootInfo };
'failover.status': { response: { status: FailoverStatus } };
'group.query': { response: Group };
'pool.query': { response: Pool };
'reporting.realtime': { response: ReportingRealtimeUpdate };
'service.query': { response: Service };
'smart.test.progress': { response: SmartTestProgressUpdate };
'system.reboot.info': { response: SystemRebootInfo };
'truecommand.config': { response: TrueCommandConfig };
'user.query': { response: User };
'vm.query': { response: VirtualMachine };
Expand Down
5 changes: 2 additions & 3 deletions src/app/interfaces/api/api-job-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ export interface ApiJobDirectory {
// Failover
'failover.reboot.other_node': { params: void; response: void };
'failover.upgrade': { params: [FailoverUpgradeParams]; response: boolean };
'failover.upgrade_finish': { params: void; response: boolean };

// Filesystem
'filesystem.put': { params: FilesystemPutParams; response: boolean };
Expand Down Expand Up @@ -168,8 +167,8 @@ export interface ApiJobDirectory {
'support.new_ticket': { params: [CreateNewTicket]; response: NewTicketResponse };

// System
'system.reboot': { params: { delay?: number }; response: void };
'system.shutdown': { params: { delay?: number }; response: void };
'system.reboot': { params: { delay?: number; reason: string }; response: void };
'system.shutdown': { params: { delay?: number; reason: string }; response: void };
'system.security.update': { params: [SystemSecurityConfig]; response: void };

// SystemDataset
Expand Down
14 changes: 14 additions & 0 deletions src/app/interfaces/reboot-info.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface SystemRebootInfo {
boot_id: string;
reboot_required_reasons: RebootRequiredReasons[];
}

export interface FailoverRebootInfo {
this_node: SystemRebootInfo;
other_node: SystemRebootInfo | null;
}

export interface RebootRequiredReasons {
code: string;
reason: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ describe('AlertsPanelComponent', () => {
reasons: [],
},
isHaLicensed: true,
isUpgradePending: false,
},
},
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<h3 mat-dialog-title>
{{ 'Reboot Required' | translate }}
</h3>

<div mat-dialog-content>
@if(thisNodeRebootReasons().length) {
<div class="reasons">
<p class="dialog-message">
<span>{{ 'The local node must be rebooted because' | translate }}:</span>
</p>
@for (reason of thisNodeRebootReasons(); track reason) {
<li> {{ reason.reason }} </li>
}
</div>
}

@if(otherNodeRebootReasons().length) {
<div class="reasons">
<p class="dialog-message">
<span>{{ 'The remote node must be rebooted because' | translate }}:</span>
</p>
@for (reason of otherNodeRebootReasons(); track reason) {
<li> {{ reason.reason }} </li>
}
</div>
}
</div>

@if(thisNodeRebootReasons().length || otherNodeRebootReasons().length) {
<ix-form-actions mat-dialog-actions class="form-actions">
<ix-checkbox
class="confirm"
[formControl]="form.controls.confirm"
[label]="'Confirm' | translate"
[required]="true"
></ix-checkbox>

@if(thisNodeRebootReasons().length) {
<button
mat-button
color="primary"
ixTest="reboot-local"
[disabled]="form.invalid"
(click)="rebootLocalNode()"
>
{{ 'Reboot Local' | translate }}
</button>
}

@if(otherNodeRebootReasons().length) {
<button
mat-button
color="primary"
ixTest="reboot-remote"
[disabled]="form.invalid"
(click)="rebootRemoteNode()"
>
{{ 'Reboot Remote' | translate }}
</button>
}
</ix-form-actions>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.dialog-message {
margin-bottom: 5px;
margin-top: 0;
padding: 0;
}

.reasons {
padding: 0 12px 12px;
}

.confirm {
margin-right: auto;
padding-right: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { provideMockStore } from '@ngrx/store/testing';
import { SystemRebootInfo } from 'app/interfaces/reboot-info.interface';
import { RebootRequiredDialogComponent } from 'app/modules/dialog/components/reboot-required-dialog/reboot-required-dialog.component';
import {
selectOtherNodeRebootInfo,
selectThisNodeRebootInfo,
} from 'app/store/reboot-info/reboot-info.selectors';

const fakeThisNodeRebootInfo: SystemRebootInfo = {
boot_id: 'this-boot-id',
reboot_required_reasons: [
{ code: 'FIPS', reason: 'Test Reason 1' },
{ code: 'FIPS', reason: 'Test Reason 2' },
],
};

const fakeOtherNodeRebootInfo: SystemRebootInfo = {
boot_id: 'other-boot-id',
reboot_required_reasons: [
{ code: 'FIPS', reason: 'Test Reason 3' },
{ code: 'FIPS', reason: 'Test Reason 4' },
],
};

describe('RebootRequiredDialogComponent', () => {
let spectator: Spectator<RebootRequiredDialogComponent>;
const createComponent = createComponentFactory({
component: RebootRequiredDialogComponent,
providers: [
provideMockStore({
selectors: [
{
selector: selectThisNodeRebootInfo,
value: fakeThisNodeRebootInfo,
},
{
selector: selectOtherNodeRebootInfo,
value: fakeOtherNodeRebootInfo,
},
],
}),
],
});

beforeEach(() => {
spectator = createComponent();
});

it('shows reasons', () => {
expect(
spectator.queryAll('.reasons li').map((item) => item.textContent.trim()),
).toEqual([
'Test Reason 1',
'Test Reason 2',
'Test Reason 3',
'Test Reason 4',
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { CdkScrollable } from '@angular/cdk/scrolling';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { MatDialogActions, MatDialogContent, MatDialogTitle } from '@angular/material/dialog';
import { FormBuilder } from '@ngneat/reactive-forms';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Store } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
import { map } from 'rxjs';
import { RebootRequiredReasons } from 'app/interfaces/reboot-info.interface';
import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component';
import { FipsService } from 'app/services/fips.service';
import { AppsState } from 'app/store';
import { selectOtherNodeRebootInfo, selectThisNodeRebootInfo } from 'app/store/reboot-info/reboot-info.selectors';

@UntilDestroy()
@Component({
selector: 'ix-reboot-required-dialog',
templateUrl: './reboot-required-dialog.component.html',
styleUrls: ['./reboot-required-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
CdkScrollable,
MatDialogContent,
MatDialogTitle,
TranslateModule,
ReactiveFormsModule,
MatDialogActions,
IxCheckboxComponent,
MatButton,
],
})
export class RebootRequiredDialogComponent {
thisNodeRebootReasons = toSignal(this.store$.select(selectThisNodeRebootInfo).pipe(
map((info) => info?.reboot_required_reasons || []),
));
otherNodeRebootReasons = toSignal(this.store$.select(selectOtherNodeRebootInfo).pipe(
map((info) => info?.reboot_required_reasons || []),
));

form = this.fb.group({
confirm: [false, Validators.requiredTrue],
});

constructor(
private store$: Store<AppsState>,
private fips: FipsService,
private fb: FormBuilder,
) {}

typeReasons(reasons: unknown): RebootRequiredReasons[] {
return reasons as RebootRequiredReasons[];
}

rebootLocalNode(): void {
this.fips.restart();
}

rebootRemoteNode(): void {
this.fips.restartRemote().pipe(untilDestroyed(this)).subscribe();
}
}

This file was deleted.

This file was deleted.

Loading
Loading