CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/compute/cloud/hyperstack/config.tsx
Views: 687
1
import type {
2
State,
3
HyperstackConfiguration,
4
ComputeServerTemplate,
5
} from "@cocalc/util/db-schema/compute-servers";
6
import { Divider, Spin, Table } from "antd";
7
import {
8
getHyperstackPriceData,
9
setServerConfiguration,
10
} from "@cocalc/frontend/compute/api";
11
import { useEffect, useState } from "react";
12
import ShowError from "@cocalc/frontend/components/error";
13
import Proxy from "@cocalc/frontend/compute/proxy";
14
import { useImages } from "@cocalc/frontend/compute/images-hook";
15
import type { HyperstackPriceData } from "@cocalc/util/compute/cloud/hyperstack/pricing";
16
import computeCost from "@cocalc/util/compute/cloud/hyperstack/compute-cost";
17
import { currency } from "@cocalc/util/misc";
18
import CostOverview from "@cocalc/frontend/compute/cost-overview";
19
import { Icon } from "@cocalc/frontend/components/icon";
20
//import GPU from "./gpu";
21
import MachineType from "./machine-type";
22
import Specs from "./specs";
23
import Image from "./image";
24
import Disk from "./disk";
25
import Ephemeral from "@cocalc/frontend/compute/ephemeral";
26
import ExcludeFromSync from "@cocalc/frontend/compute/exclude-from-sync";
27
import { useTypedRedux } from "@cocalc/frontend/app-framework";
28
import DNS from "@cocalc/frontend/compute/cloud/common/dns";
29
import AllowCollaboratorControl from "@cocalc/frontend/compute/allow-collaborator-control";
30
import Template from "@cocalc/frontend/compute/cloud/common/template";
31
import { A } from "@cocalc/frontend/components/A";
32
33
interface Props {
34
configuration: HyperstackConfiguration;
35
editable?: boolean;
36
// if id not set, then doesn't try to save anything to the backend
37
id?: number;
38
project_id: string;
39
// called whenever changes are made.
40
onChange?: (configuration: HyperstackConfiguration) => void;
41
disabled?: boolean;
42
state?: State;
43
data?;
44
setCloud?;
45
template?: ComputeServerTemplate;
46
}
47
48
export default function HyperstackConfig({
49
configuration: configuration0,
50
editable,
51
id,
52
project_id,
53
onChange,
54
disabled,
55
state,
56
data,
57
setCloud,
58
template,
59
}: Props) {
60
const [priceData, setPriceData] = useState<HyperstackPriceData | null>(null);
61
const [IMAGES, ImagesError] = useImages();
62
const [loading, setLoading] = useState<boolean>(false);
63
const [error, setError] = useState<string>("");
64
const [configuration, setLocalConfiguration] =
65
useState<HyperstackConfiguration>(configuration0);
66
const [cost, setCost] = useState<number | null>(null);
67
state = state ?? "deprovisioned";
68
69
useEffect(() => {
70
(async () => {
71
try {
72
setLoading(true);
73
const data = await getHyperstackPriceData();
74
//window.x = { priceData: data };
75
setPriceData(data);
76
} catch (err) {
77
setError(`${err}`);
78
} finally {
79
setLoading(false);
80
}
81
})();
82
}, []);
83
84
useEffect(() => {
85
if (!editable || configuration == null || priceData == null) {
86
return;
87
}
88
try {
89
const cost = computeCost({ configuration, priceData });
90
setCost(cost);
91
} catch (err) {
92
setError(`${err}`);
93
setCost(null);
94
}
95
}, [configuration, priceData]);
96
97
useEffect(() => {
98
if (!editable) {
99
setLocalConfiguration(configuration0);
100
}
101
}, [configuration0]);
102
103
if (!editable || !project_id) {
104
return (
105
<Specs
106
flavor_name={configuration.flavor_name}
107
region_name={configuration.region_name}
108
priceData={priceData}
109
diskSizeGb={configuration.diskSizeGb}
110
/>
111
);
112
}
113
114
if (ImagesError != null) {
115
return ImagesError;
116
}
117
118
const setConfig = async (changes) => {
119
try {
120
const newConfiguration = { ...configuration, ...changes };
121
setLoading(true);
122
if (onChange != null) {
123
onChange(newConfiguration);
124
}
125
setLocalConfiguration(newConfiguration);
126
if (id != null) {
127
await setServerConfiguration({ id, configuration: changes });
128
}
129
} catch (err) {
130
setError(`${err}`);
131
} finally {
132
setLoading(false);
133
}
134
};
135
136
const columns = [
137
{
138
dataIndex: "value",
139
key: "value",
140
},
141
];
142
143
const dataSource = [
144
// {
145
// key: "gpu",
146
147
// value: (
148
// <GPU
149
// state={state}
150
// disabled={
151
// loading || disabled || (state != "deprovisioned" && state != "off")
152
// }
153
// priceData={priceData}
154
// setConfig={setConfig}
155
// configuration={configuration}
156
// />
157
// ),
158
// },
159
{
160
key: "machine",
161
value: (
162
<>
163
<div style={{ marginBottom: "5px" }}>
164
<b style={{ color: "#666" }}>Machine Type</b>
165
<br />
166
</div>
167
<MachineType
168
setConfig={setConfig}
169
setCloud={setCloud}
170
configuration={configuration}
171
state={state}
172
disabled={
173
loading ||
174
disabled ||
175
(state != "deprovisioned" && state != "off")
176
}
177
priceData={priceData}
178
/>
179
</>
180
),
181
},
182
// {
183
// key: "provisioning",
184
// value: <Provisioning />,
185
// },
186
{
187
key: "image",
188
value: (
189
<Image
190
state={state}
191
disabled={loading || disabled}
192
setConfig={setConfig}
193
configuration={configuration}
194
/>
195
),
196
},
197
{
198
key: "disk",
199
value: (
200
<Disk
201
id={id}
202
disabled={loading}
203
setConfig={setConfig}
204
configuration={configuration}
205
data={data}
206
priceData={priceData}
207
state={state}
208
IMAGES={IMAGES}
209
/>
210
),
211
},
212
{
213
key: "exclude",
214
value: (
215
<ExcludeFromSync
216
id={id}
217
disabled={loading}
218
setConfig={setConfig}
219
configuration={configuration}
220
state={state}
221
style={{ marginTop: "10px", color: "#666" }}
222
/>
223
),
224
},
225
{
226
key: "dns",
227
value: (
228
<div>
229
<Icon name="network" /> <b style={{ color: "#666" }}>Domain Name</b>
230
<DNS
231
setConfig={setConfig}
232
configuration={configuration}
233
loading={loading}
234
/>
235
</div>
236
),
237
},
238
{
239
key: "proxy",
240
value: (
241
<Proxy
242
id={id}
243
project_id={project_id}
244
setConfig={setConfig}
245
configuration={configuration}
246
data={data}
247
state={state}
248
IMAGES={IMAGES}
249
/>
250
),
251
},
252
{
253
key: "ephemeral",
254
label: <></>,
255
value: (
256
<Ephemeral
257
setConfig={setConfig}
258
configuration={configuration}
259
loading={loading}
260
/>
261
),
262
},
263
{
264
key: "allow-collaborator-control",
265
label: <></>,
266
value: (
267
<AllowCollaboratorControl
268
setConfig={setConfig}
269
configuration={configuration}
270
loading={loading}
271
/>
272
),
273
},
274
{
275
key: "admin",
276
label: <></>,
277
value: (
278
<Admin id={id} configuration={configuration} template={template} />
279
),
280
},
281
];
282
283
const showError = (
284
<ShowError
285
error={error}
286
setError={setError}
287
style={{ width: "100%", margin: "5px 0" }}
288
/>
289
);
290
291
return (
292
<div style={{ marginBottom: "30px" }}>
293
<div style={{ color: "#666", marginBottom: "10px" }}>
294
{showError}
295
{loading && (
296
<div style={{ textAlign: "center" }}>
297
<Spin style={{ marginLeft: "15px" }} />
298
</div>
299
)}
300
{cost != null && priceData != null && (
301
<CostOverview
302
cost={cost}
303
description={
304
<>
305
You pay <b>{currency(cost)}/hour</b> while the server is running
306
for compute and storage. You only pay{" "}
307
<b>
308
{currency(
309
computeCost({ configuration, priceData, state: "off" }),
310
)}
311
/hour
312
</b>{" "}
313
for storage when the server is off, and there is no cost when
314
the server is deprovisioned. All network data transfer{" "}
315
<b>is free</b>, and{" "}
316
<A href="https://www.hyperstack.cloud/why-hyperstack">
317
Hyperstack's data centers are 100% Renewably Powered
318
</A>{" "}
319
via hydro-energy, housed within{" "}
320
<A href="https://www.hyperstack.cloud/blog/company-news/nexgen-cloud-and-aq-compute-advance-towards-net-zero-ai-supercloud">
321
sustainable data centers
322
</A>
323
.
324
</>
325
}
326
/>
327
)}
328
<Divider />
329
<div style={{ textAlign: "center", margin: "10px 80px" }}>
330
<Specs
331
flavor_name={configuration.flavor_name}
332
region_name={configuration.region_name}
333
priceData={priceData}
334
diskSizeGb={configuration.diskSizeGb}
335
/>
336
</div>
337
<Divider />
338
<Table
339
showHeader={false}
340
style={{ marginTop: "5px" }}
341
columns={columns}
342
dataSource={dataSource}
343
pagination={false}
344
/>
345
</div>
346
347
{showError}
348
</div>
349
);
350
}
351
352
function Admin({ id, configuration, template }) {
353
const isAdmin = useTypedRedux("account", "is_admin");
354
if (!isAdmin) {
355
return null;
356
}
357
return (
358
<div>
359
<div style={{ color: "#666", marginBottom: "5px" }}>
360
<b>
361
<Icon name="users" /> Admin
362
</b>
363
<br />
364
Settings and functionality only available to admins.
365
<br />
366
<pre>
367
id={id}, configuration={JSON.stringify(configuration, undefined, 2)}
368
</pre>
369
<Template id={id} template={template} />
370
</div>
371
</div>
372
);
373
}
374
375