diff --git a/models/profiler_type.go b/models/profiler_type.go
deleted file mode 100644
index 9214ffd39..000000000
--- a/models/profiler_type.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// Code generated by go-swagger; DO NOT EDIT.
-
-// This file is part of MinIO Console Server
-// Copyright (c) 2022 MinIO, Inc.
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-//
-
-package models
-
-// This file was generated by the swagger tool.
-// Editing this file might prove futile when you re-run the swagger generate command
-
-import (
- "context"
- "encoding/json"
-
- "github.com/go-openapi/errors"
- "github.com/go-openapi/strfmt"
- "github.com/go-openapi/validate"
-)
-
-// ProfilerType profiler type
-//
-// swagger:model profilerType
-type ProfilerType string
-
-func NewProfilerType(value ProfilerType) *ProfilerType {
- return &value
-}
-
-// Pointer returns a pointer to a freshly-allocated ProfilerType.
-func (m ProfilerType) Pointer() *ProfilerType {
- return &m
-}
-
-const (
-
- // ProfilerTypeCPU captures enum value "cpu"
- ProfilerTypeCPU ProfilerType = "cpu"
-
- // ProfilerTypeMem captures enum value "mem"
- ProfilerTypeMem ProfilerType = "mem"
-
- // ProfilerTypeBlock captures enum value "block"
- ProfilerTypeBlock ProfilerType = "block"
-
- // ProfilerTypeMutex captures enum value "mutex"
- ProfilerTypeMutex ProfilerType = "mutex"
-
- // ProfilerTypeTrace captures enum value "trace"
- ProfilerTypeTrace ProfilerType = "trace"
-
- // ProfilerTypeThreads captures enum value "threads"
- ProfilerTypeThreads ProfilerType = "threads"
-
- // ProfilerTypeGoroutines captures enum value "goroutines"
- ProfilerTypeGoroutines ProfilerType = "goroutines"
-)
-
-// for schema
-var profilerTypeEnum []interface{}
-
-func init() {
- var res []ProfilerType
- if err := json.Unmarshal([]byte(`["cpu","mem","block","mutex","trace","threads","goroutines"]`), &res); err != nil {
- panic(err)
- }
- for _, v := range res {
- profilerTypeEnum = append(profilerTypeEnum, v)
- }
-}
-
-func (m ProfilerType) validateProfilerTypeEnum(path, location string, value ProfilerType) error {
- if err := validate.EnumCase(path, location, value, profilerTypeEnum, true); err != nil {
- return err
- }
- return nil
-}
-
-// Validate validates this profiler type
-func (m ProfilerType) Validate(formats strfmt.Registry) error {
- var res []error
-
- // value enum
- if err := m.validateProfilerTypeEnum("", "body", m); err != nil {
- return err
- }
-
- if len(res) > 0 {
- return errors.CompositeValidationError(res...)
- }
- return nil
-}
-
-// ContextValidate validates this profiler type based on context it is used
-func (m ProfilerType) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
- return nil
-}
diff --git a/models/profiling_start_request.go b/models/profiling_start_request.go
index 8a84938f1..1f21b7784 100644
--- a/models/profiling_start_request.go
+++ b/models/profiling_start_request.go
@@ -38,7 +38,7 @@ type ProfilingStartRequest struct {
// type
// Required: true
- Type *ProfilerType `json:"type"`
+ Type *string `json:"type"`
}
// Validate validates this profiling start request
@@ -61,51 +61,11 @@ func (m *ProfilingStartRequest) validateType(formats strfmt.Registry) error {
return err
}
- if err := validate.Required("type", "body", m.Type); err != nil {
- return err
- }
-
- if m.Type != nil {
- if err := m.Type.Validate(formats); err != nil {
- if ve, ok := err.(*errors.Validation); ok {
- return ve.ValidateName("type")
- } else if ce, ok := err.(*errors.CompositeError); ok {
- return ce.ValidateName("type")
- }
- return err
- }
- }
-
return nil
}
-// ContextValidate validate this profiling start request based on the context it is used
+// ContextValidate validates this profiling start request based on context it is used
func (m *ProfilingStartRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
- var res []error
-
- if err := m.contextValidateType(ctx, formats); err != nil {
- res = append(res, err)
- }
-
- if len(res) > 0 {
- return errors.CompositeValidationError(res...)
- }
- return nil
-}
-
-func (m *ProfilingStartRequest) contextValidateType(ctx context.Context, formats strfmt.Registry) error {
-
- if m.Type != nil {
- if err := m.Type.ContextValidate(ctx, formats); err != nil {
- if ve, ok := err.(*errors.Validation); ok {
- return ve.ValidateName("type")
- } else if ce, ok := err.(*errors.CompositeError); ok {
- return ce.ValidateName("type")
- }
- return err
- }
- }
-
return nil
}
diff --git a/portal-ui/src/screens/Console/Support/Profile.tsx b/portal-ui/src/screens/Console/Support/Profile.tsx
new file mode 100644
index 000000000..854bfa8af
--- /dev/null
+++ b/portal-ui/src/screens/Console/Support/Profile.tsx
@@ -0,0 +1,199 @@
+import React, { Fragment, useState } from "react";
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { Button, Grid } from "@mui/material";
+import PageHeader from "../Common/PageHeader/PageHeader";
+import PageLayout from "../Common/Layout/PageLayout";
+import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
+import api from "../../../common/api";
+import { ErrorResponseHandler } from "../../../common/types";
+import {
+ actionsTray,
+ containerForHeader,
+ inlineCheckboxes,
+} from "../Common/FormComponents/common/styleLibrary";
+import HelpBox from "../../../common/HelpBox";
+import WarnIcon from "../../../icons/WarnIcon";
+
+const styles = (theme: Theme) =>
+ createStyles({
+ buttonContainer: {
+ marginTop: 24,
+ textAlign: "right",
+ "& .MuiButton-root": {
+ marginLeft: 8,
+ },
+ },
+ dropdown: {
+ marginBottom: 24,
+ },
+ checkboxLabel: {
+ marginTop: 12,
+ marginRight: 4,
+ fontSize: 16,
+ fontWeight: 500,
+ },
+ checkboxDisabled: {
+ opacity: 0.5,
+ },
+ inlineCheckboxes: {
+ ...inlineCheckboxes.inlineCheckboxes,
+ alignItems: "center",
+
+ "@media (max-width: 900px)": {
+ flexFlow: "column",
+ alignItems: "flex-start",
+ },
+ },
+ ...actionsTray,
+ ...containerForHeader(theme.spacing(4)),
+ });
+
+interface IProfileProps {
+ classes: any;
+}
+
+const Profile = ({ classes }: IProfileProps) => {
+ const [profilingStarted, setProfilingStarted] = useState(false);
+ const [types, setTypes] = useState([
+ "cpu",
+ "mem",
+ "block",
+ "mutex",
+ "trace",
+ "threads",
+ "goroutines",
+ ]);
+
+ const typesList = [
+ { label: "cpu", value: "cpu" },
+ { label: "mem", value: "mem" },
+ { label: "block", value: "block" },
+ { label: "mutex", value: "mutex" },
+ { label: "trace", value: "trace" },
+ { label: "threads", value: "threads" },
+ { label: "goroutines", value: "goroutines" },
+ ];
+
+ const onCheckboxClick = (e: React.ChangeEvent) => {
+ let newArr: string[] = [];
+ if (types.indexOf(e.target.value) > -1) {
+ newArr = types.filter((type) => type !== e.target.value);
+ } else {
+ newArr = [...types, e.target.value];
+ }
+ setTypes(newArr);
+ };
+
+ const startProfiling = () => {
+ if (!profilingStarted) {
+ const typeString = types.join(",");
+ setProfilingStarted(true);
+ api
+ .invoke("POST", `/api/v1/profiling/start`, {
+ type: typeString,
+ })
+ .then(() => {})
+ .catch((err: ErrorResponseHandler) => {
+ console.log(err);
+ setProfilingStarted(false);
+ });
+ }
+ };
+
+ const stopProfiling = () => {
+ if (profilingStarted) {
+ const anchor = document.createElement("a");
+ document.body.appendChild(anchor);
+ let path = "/api/v1/profiling/stop";
+ var req = new XMLHttpRequest();
+ req.open("POST", path, true);
+ req.responseType = "blob";
+ req.onreadystatechange = () => {
+ if (req.readyState === 4 && req.status === 200) {
+ let filename = "profile.zip";
+ setProfilingStarted(false);
+ var link = document.createElement("a");
+ link.href = window.URL.createObjectURL(req.response);
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ }
+ };
+ req.send();
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ Types to profile:
+ {typesList.map((t) => (
+ -1}
+ disabled={profilingStarted}
+ key={`checkbox-${t.label}`}
+ id={`checkbox-${t.label}`}
+ label={t.label}
+ name={`checkbox-${t.label}`}
+ onChange={onCheckboxClick}
+ value={t.value}
+ />
+ ))}
+
+
+
+
+
+
+
+ {!profilingStarted && (
+
+
+ }
+ help={}
+ />
+
+ )}
+
+
+ );
+};
+
+export default withStyles(styles)(Profile);
diff --git a/portal-ui/src/screens/Console/Tools/Tools.tsx b/portal-ui/src/screens/Console/Tools/Tools.tsx
index 834c14d7a..fc4c4071d 100644
--- a/portal-ui/src/screens/Console/Tools/Tools.tsx
+++ b/portal-ui/src/screens/Console/Tools/Tools.tsx
@@ -27,6 +27,7 @@ import withSuspense from "../Common/Components/withSuspense";
const Inspect = withSuspense(React.lazy(() => import("./Inspect")));
const Register = withSuspense(React.lazy(() => import("../Support/Register")));
+const Profile = withSuspense(React.lazy(() => import("../Support/Profile")));
const Tools = () => {
return (
@@ -34,6 +35,7 @@ const Tools = () => {
+