DashboardPage.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import {
  2. DocumentTitle,
  3. K8sResourceCommon,
  4. k8sPatch,
  5. useK8sModel,
  6. useK8sWatchResource,
  7. } from '@openshift-console/dynamic-plugin-sdk';
  8. import {
  9. Alert,
  10. Card,
  11. CardBody,
  12. CardTitle,
  13. PageSection,
  14. Spinner,
  15. Switch,
  16. Title,
  17. } from '@patternfly/react-core';
  18. import type { FC } from 'react';
  19. import { useState } from 'react';
  20. const TUNED_NAMESPACE = 'openshift-cluster-node-tuning-operator';
  21. const TUNED_NAME = 'custom-tuned-profile';
  22. const POWERSAVE_PROFILE = 'homelab-powersave';
  23. const PERFORMANCE_PROFILE = 'homelab-performance';
  24. const TunedGVK = {
  25. group: 'tuned.openshift.io',
  26. version: 'v1',
  27. kind: 'Tuned',
  28. };
  29. interface TunedResource extends K8sResourceCommon {
  30. spec?: {
  31. recommend?: Array<{
  32. profile?: string;
  33. [key: string]: unknown;
  34. }>;
  35. };
  36. }
  37. const DashboardPage: FC = () => {
  38. const [patching, setPatching] = useState(false);
  39. const [patchError, setPatchError] = useState<string>();
  40. const [tunedModel, modelLoading] = useK8sModel(TunedGVK);
  41. const [tuned, loaded, loadError] =
  42. useK8sWatchResource<TunedResource>({
  43. groupVersionKind: TunedGVK,
  44. name: TUNED_NAME,
  45. namespace: TUNED_NAMESPACE,
  46. });
  47. const recommendations = tuned?.spec?.recommend ?? [];
  48. const profileIndex = recommendations.findIndex(
  49. (recommendation) =>
  50. recommendation.profile === POWERSAVE_PROFILE ||
  51. recommendation.profile === PERFORMANCE_PROFILE,
  52. );
  53. const currentProfile =
  54. profileIndex >= 0
  55. ? recommendations[profileIndex]?.profile
  56. : undefined;
  57. const performanceEnabled =
  58. currentProfile === PERFORMANCE_PROFILE;
  59. const profileKnown =
  60. currentProfile === POWERSAVE_PROFILE ||
  61. currentProfile === PERFORMANCE_PROFILE;
  62. const changePerformanceMode = async (enabled: boolean) => {
  63. if (!tunedModel || !tuned || profileIndex < 0) {
  64. return;
  65. }
  66. setPatching(true);
  67. setPatchError(undefined);
  68. try {
  69. await k8sPatch({
  70. model: tunedModel,
  71. resource: tuned,
  72. data: [
  73. {
  74. op: 'replace',
  75. path: `/spec/recommend/${profileIndex}/profile`,
  76. value: enabled
  77. ? PERFORMANCE_PROFILE
  78. : POWERSAVE_PROFILE,
  79. },
  80. ],
  81. });
  82. } catch (error) {
  83. setPatchError(
  84. error instanceof Error
  85. ? error.message
  86. : String(error),
  87. );
  88. } finally {
  89. setPatching(false);
  90. }
  91. };
  92. return (
  93. <>
  94. <DocumentTitle>Dashboard</DocumentTitle>
  95. <PageSection>
  96. <Title headingLevel="h1" size="2xl">
  97. Homelab Dashboard
  98. </Title>
  99. </PageSection>
  100. <PageSection>
  101. <Card>
  102. <CardTitle>Cluster performance</CardTitle>
  103. <CardBody>
  104. {!loaded || modelLoading ? (
  105. <Spinner />
  106. ) : loadError ? (
  107. <Alert
  108. isInline
  109. variant="danger"
  110. title="Unable to read TuneD configuration"
  111. >
  112. {String(loadError)}
  113. </Alert>
  114. ) : (
  115. <>
  116. <Switch
  117. id="cluster-performance-mode"
  118. label="Performance mode"
  119. isChecked={performanceEnabled}
  120. isDisabled={patching || !profileKnown}
  121. onChange={(_, checked) =>
  122. void changePerformanceMode(checked)
  123. }
  124. /><br />
  125. <p>
  126. Current profile:{' '}
  127. <strong>
  128. {currentProfile ?? 'Unknown'}
  129. </strong>
  130. </p>
  131. {patching && <Spinner size="md" />}
  132. {!profileKnown && (
  133. <Alert
  134. isInline
  135. variant="warning"
  136. title="Unknown TuneD profile"
  137. >
  138. Expected {POWERSAVE_PROFILE} or{' '}
  139. {PERFORMANCE_PROFILE}.
  140. </Alert>
  141. )}
  142. {patchError && (
  143. <Alert
  144. isInline
  145. variant="danger"
  146. title="Unable to change performance mode"
  147. >
  148. {patchError}
  149. </Alert>
  150. )}
  151. </>
  152. )}
  153. </CardBody>
  154. </Card>
  155. </PageSection>
  156. </>
  157. );
  158. };
  159. export default DashboardPage;