This is an automated email from the ASF dual-hosted git repository.

arshad pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/frontend-refactor by this push:
     new ce37d24d7f AMBARI-26385: Ambari Web React: Regenerate keytabs (#4065)
ce37d24d7f is described below

commit ce37d24d7f3720695db6450f55381e51353e8268
Author: Sandeep  Kumar <[email protected]>
AuthorDate: Sun Sep 14 21:46:02 2025 +0530

    AMBARI-26385: Ambari Web React: Regenerate keytabs (#4065)
---
 ambari-web/latest/src/components/Modal.tsx         |   2 +-
 .../src/screens/Kerberos/InvalidKdcPopup.tsx       |  94 ++++++++++++
 .../src/screens/Kerberos/RegenerateKeytabs.tsx     | 165 +++++++++++++++++++++
 3 files changed, 260 insertions(+), 1 deletion(-)

diff --git a/ambari-web/latest/src/components/Modal.tsx 
b/ambari-web/latest/src/components/Modal.tsx
index 8acbcb0749..a632851f1d 100644
--- a/ambari-web/latest/src/components/Modal.tsx
+++ b/ambari-web/latest/src/components/Modal.tsx
@@ -23,7 +23,7 @@ import classNames from "classnames";
 export type ModalProps = {
   isOpen: boolean;
   onClose: () => void;
-  modalTitle: string;
+  modalTitle: any;
   modalBody: ReactNode;
   className?:string;
   successCallback: () => void;
diff --git a/ambari-web/latest/src/screens/Kerberos/InvalidKdcPopup.tsx 
b/ambari-web/latest/src/screens/Kerberos/InvalidKdcPopup.tsx
new file mode 100644
index 0000000000..b4c1dca143
--- /dev/null
+++ b/ambari-web/latest/src/screens/Kerberos/InvalidKdcPopup.tsx
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useState } from "react";
+import Modal from "../../components/Modal";
+import { Alert, Form } from "react-bootstrap";
+import { translate } from "../../Utils/Utility";
+
+type InvalidKDCPopupProps = {
+    isOpen: boolean;
+    onClose: () => void;
+    handleSave: (adminPrincipal: string, adminPassword: string, 
saveCredentials: boolean) => void;
+};
+
+export default function InvalidKDCPopup({isOpen, onClose, handleSave}: 
InvalidKDCPopupProps): React.ReactElement {
+    const [adminPrincipal, setAdminPrincipal] = useState("");
+    const [adminPassword, setAdminPassword] = useState("");
+    const [saveCredentials, setSaveCredentials] = useState(false);
+    
+    function getModalBody() {
+        return (
+            <>
+                <Alert variant="warning">
+                Warning: Missing KDC administrator credentials. Please enter 
admin principal and password.
+                </Alert>
+                <Form>
+                    <Form.Group controlId="adminPrincipal">
+                        <Form.Label>Admin Principal</Form.Label>
+                        <Form.Control
+                        type="text"
+                        value={adminPrincipal}
+                        onChange={(e) => setAdminPrincipal(e.target.value)}
+                        />
+                    </Form.Group>
+                    <Form.Group controlId="adminPassword">
+                        <Form.Label>Admin Password</Form.Label>
+                        <Form.Control
+                        type="password"
+                        value={adminPassword}
+                        onChange={(e) => setAdminPassword(e.target.value)}
+                        />
+                    </Form.Group>
+                    <Form.Group controlId="saveCredentials">
+                        <Form.Check
+                        type="checkbox"
+                        label="Save Admin Credentials"
+                        checked={saveCredentials}
+                        onChange={(e) => setSaveCredentials(e.target.checked)}
+                        />
+                    </Form.Group>
+                </Form>
+            </>
+        )
+    }
+
+    async function handleSaveDetails() {
+        handleSave(adminPrincipal, adminPassword, saveCredentials);
+    }
+
+    return (
+        <>
+            <Modal
+                isOpen={isOpen}
+                onClose={() => onClose()}
+                modalTitle={translate("popup.invalid.KDC.header")}
+                modalBody={getModalBody()}
+                options={{
+                    okButtonText: "SAVE",
+                    cancelableViaIcon: true,
+                    cancelableViaBtn: true,
+                    modalSize: "modal-md",
+                }}
+                successCallback={() => {
+                    handleSaveDetails();
+                }}
+            />
+        </>
+    )
+}
\ No newline at end of file
diff --git a/ambari-web/latest/src/screens/Kerberos/RegenerateKeytabs.tsx 
b/ambari-web/latest/src/screens/Kerberos/RegenerateKeytabs.tsx
new file mode 100644
index 0000000000..212b1ba9b2
--- /dev/null
+++ b/ambari-web/latest/src/screens/Kerberos/RegenerateKeytabs.tsx
@@ -0,0 +1,165 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext, useEffect, useRef, useState } from "react";
+import { ProgressStatus } from "../../constants";
+import usePolling from "../../hooks/usePolling";
+import { isFinished } from "../../Utils/Utility";
+import { AppContext } from "../../store/context";
+import { RequestApi } from "../../api/requestApi";
+import KerberosApi from "../../api/kerberosApi";
+import InvalidKDCPopup from "./InvalidKdcPopup";
+
+type RegenerateKeytabsProps = {
+  missingHostCheck: boolean;
+  restartComponentsCheck: boolean;
+};
+
+export default function RegenerateKeytabs({
+  missingHostCheck,
+  restartComponentsCheck,
+}: RegenerateKeytabsProps) {
+  // @ts-ignore
+  const [showBgOperation, setShowBgOperation] = useState(false);
+  const [showInvalidKDCPopup, setShowInvalidKDCPopup] = useState(false);
+  const requestId = useRef<string | number>("");
+  const restartCheckRef = useRef(restartComponentsCheck);
+  const { clusterName } = useContext(AppContext);
+
+  const { stopPolling, pausePolling, resumePolling } = usePolling(
+    getRequestStatus,
+    3000
+  );
+
+  useEffect(() => {
+    regenerate();
+  }, []);
+
+  useEffect(() => {
+    if (restartComponentsCheck) {
+      resumePolling();
+    } else {
+      pausePolling();
+    }
+  }, [restartComponentsCheck]);
+
+  async function getRequestStatus() {
+    if (!restartCheckRef.current) pausePolling();
+
+    const requestStatus: any = await RequestApi.getRequestStatus(
+      clusterName,
+      requestId.current as string
+    );
+    const { Requests } = requestStatus;
+    if (isFinished(Requests.request_status)) {
+      if (Requests.request_status === ProgressStatus.COMPLETED) {
+        stopPolling();
+        if (restartCheckRef.current) restartComponents();
+      }
+    }
+  }
+
+  async function restartComponents() {
+    const restartPayload = {
+      RequestInfo: {
+        command: "RESTART",
+        context: "Restart all services",
+        operation_level: "host_component",
+      },
+      "Requests/resource_filters": [
+        {
+          hosts_predicate: `HostRoles/cluster_name=${clusterName}`,
+        },
+      ],
+    };
+    const requestData = await RequestApi.postRequest(
+      clusterName,
+      restartPayload
+    );
+    requestId.current = requestData.Requests.id;
+  }
+
+  async function regenerate() {
+    const payload = {
+      Clusters: {
+        security_type: "KERBEROS",
+      },
+    };
+    try {
+      const params = missingHostCheck
+        ? "regenerate_keytabs=missing"
+        : "regenerate_keytabs=all";
+      const requestData = await RequestApi.regenerateKeytabs(
+        clusterName,
+        payload,
+        params
+      );
+      requestId.current = requestData.Requests.id;
+      if (requestId.current !== "") setShowBgOperation(true);
+    } catch (error) {
+      console.log("Error regenerating keytabs: ", error);
+      setShowInvalidKDCPopup(true);
+    }
+  }
+
+  const handleSaveInvalidKDC = async (
+    adminPrincipal: string,
+    adminPassword: string,
+    saveCredentials: boolean
+  ) => {
+    setShowInvalidKDCPopup(false);
+    const payload = {
+      Credential: {
+        key: adminPassword,
+        principal: adminPrincipal,
+        type: saveCredentials ? "persisted" : "temporary",
+      },
+    };
+
+    try {
+      await KerberosApi.postKDCAdminCredentials(
+        clusterName,
+        payload
+      );
+
+      // Retry the regenerateKeytabs API call with the provided credentials
+      await regenerate();
+    } catch (error) {
+      console.error("Error posting KDC Admin Credentials:", error);
+    }
+  };
+
+  return (
+    <>
+      {/* {showBgOperation ? (
+        <BackgroundOperations
+          isOpen={showBgOperation}
+          onClose={() => setShowBgOperation(false)}
+          rootLevel={ViewLevel.REQUESTS}
+          requestId={requestId.current}
+        />
+      ) : null} */}
+
+      <InvalidKDCPopup
+        isOpen={showInvalidKDCPopup}
+        onClose={() => setShowInvalidKDCPopup(false)}
+        handleSave={handleSaveInvalidKDC}
+      />
+    </>
+  );
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to