From e40b4c56cfe35eb8848dfa348c4d195432b02b4b Mon Sep 17 00:00:00 2001 From: Aashish Sharma Date: Fri, 12 Jul 2024 16:20:15 +0530 Subject: [PATCH] mgr/dashboard: Add progress bar to rgw multisite automation wizard Fixes: https://tracker.ceph.com/issues/67829 Fixes: https://tracker.ceph.com/issues/67830 Signed-off-by: Aashish Sharma --- src/pybind/mgr/dashboard/controllers/auth.py | 21 +- src/pybind/mgr/dashboard/controllers/rgw.py | 31 +- .../upgrade-progress.component.html | 69 +--- .../rgw-multisite-details.component.html | 40 +- .../rgw-multisite-details.component.ts | 30 +- .../multisite-wizard-steps.enum.ts | 19 + .../rgw-multisite-wizard.component.html | 232 ++++++++--- .../rgw-multisite-wizard.component.ts | 153 +++---- .../frontend/src/app/ceph/rgw/rgw.module.ts | 6 +- .../app/shared/api/rgw-multisite.service.ts | 12 +- .../shared/components/components.module.ts | 7 +- .../progress/progress.component.html | 62 +++ .../progress/progress.component.scss | 0 .../progress/progress.component.spec.ts | 22 + .../components/progress/progress.component.ts | 21 + .../components/wizard/wizard.component.ts | 14 +- .../mgr/dashboard/services/rgw_client.py | 378 ++++++++++-------- src/pybind/mgr/dashboard/services/service.py | 54 ++- src/pybind/mgr/rgw/module.py | 2 +- 19 files changed, 765 insertions(+), 408 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/multisite-wizard-steps.enum.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/progress/progress.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/progress/progress.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/progress/progress.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/progress/progress.component.ts diff --git a/src/pybind/mgr/dashboard/controllers/auth.py b/src/pybind/mgr/dashboard/controllers/auth.py index 16276af17e4c4..951486572d90e 100644 --- a/src/pybind/mgr/dashboard/controllers/auth.py +++ b/src/pybind/mgr/dashboard/controllers/auth.py @@ -66,7 +66,7 @@ class Auth(RESTController, ControllerAuthMixin): fsid = mgr.get('config')['fsid'] except KeyError: fsid = '' - if max_attempt == 0 or mgr.ACCESS_CTRL_DB.get_attempt(username) < max_attempt: + if max_attempt == 0 or mgr.ACCESS_CTRL_DB.get_attempt(username) < max_attempt: # pylint: disable=R1702,line-too-long # noqa: E501 if user_data: user_perms = user_data.get('permissions') pwd_expiration_date = user_data.get('pwdExpirationDate', None) @@ -94,20 +94,26 @@ class Auth(RESTController, ControllerAuthMixin): multicluster_config = Settings.MULTICLUSTER_CONFIG.copy() try: if fsid in multicluster_config['config']: - existing_entries = multicluster_config['config'][fsid] - if not any((entry['user'] == username or entry['cluster_alias'] == 'local-cluster') for entry in existing_entries): # noqa E501 #pylint: disable=line-too-long - existing_entries.append({ + cluster_configurations = multicluster_config['config'][fsid] + for config_item in cluster_configurations: + if config_item['user'] == username or config_item['cluster_alias'] == 'local-cluster': # noqa E501 #pylint: disable=line-too-long + config_item['token'] = token # Update token + break + else: + cluster_configurations.append({ "name": fsid, "url": origin, "cluster_alias": "local-cluster", - "user": username + "user": username, + "token": token }) else: multicluster_config['config'][fsid] = [{ "name": fsid, "url": origin, "cluster_alias": "local-cluster", - "user": username + "user": username, + "token": token }] except KeyError: @@ -121,7 +127,8 @@ class Auth(RESTController, ControllerAuthMixin): "name": fsid, "url": origin, "cluster_alias": "local-cluster", - "user": username + "user": username, + "token": token } ] } diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index 398fa341bf278..8667d469060f8 100755 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -14,8 +14,9 @@ from ..rest_client import RequestException from ..security import Permission, Scope from ..services.auth import AuthManager, JwtManager from ..services.ceph_service import CephService -from ..services.rgw_client import _SYNC_GROUP_ID, NoRgwDaemonsException, RgwClient, RgwMultisite -from ..services.service import RgwServiceManager +from ..services.rgw_client import _SYNC_GROUP_ID, NoRgwDaemonsException, \ + RgwClient, RgwMultisite, RgwMultisiteAutomation +from ..services.service import RgwServiceManager, wait_for_daemon_to_start from ..tools import json_str_to_object, str_to_bool from . import APIDoc, APIRouter, BaseController, CreatePermission, \ CRUDCollectionMethod, CRUDEndpoint, DeletePermission, Endpoint, \ @@ -118,12 +119,15 @@ class RgwMultisiteStatus(RESTController): # pylint: disable=W0102,W0613 def setup_multisite_replication(self, daemon_name=None, realm_name=None, zonegroup_name=None, zonegroup_endpoints=None, zone_name=None, zone_endpoints=None, - username=None, cluster_fsid=None): - multisite_instance = RgwMultisite() + username=None, cluster_fsid=None, replication_zone_name=None, + cluster_details=None): + multisite_instance = RgwMultisiteAutomation() result = multisite_instance.setup_multisite_replication(realm_name, zonegroup_name, zonegroup_endpoints, zone_name, zone_endpoints, username, - cluster_fsid) + cluster_fsid, + replication_zone_name, + cluster_details) return result @RESTController.Collection(method='PUT', path='/setup-rgw-credentials') @@ -131,7 +135,22 @@ class RgwMultisiteStatus(RESTController): # pylint: disable=W0102,W0613 def restart_rgw_daemons_and_set_credentials(self): rgw_service_manager_instance = RgwServiceManager() - result = rgw_service_manager_instance.restart_rgw_daemons_and_set_credentials() + result = rgw_service_manager_instance.configure_rgw_credentials() + return result + + @RESTController.Collection(method='GET', path='/available-ports') + @allow_empty_body + # pylint: disable=W0102,W0613 + def get_available_ports(self): + rgw_service_manager_instance = RgwServiceManager() + result = rgw_service_manager_instance.find_available_port() + return result + + @RESTController.Collection(method='GET', path='/check-daemons-status') + @allow_empty_body + # pylint: disable=W0102,W0613 + def check_daemons_status(self, service_name=None): + result = wait_for_daemon_to_start(service_name=service_name) return result diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/upgrade/upgrade-progress/upgrade-progress.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/upgrade/upgrade-progress/upgrade-progress.component.html index c683eee7d138e..c561594e23b19 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/upgrade/upgrade-progress/upgrade-progress.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/upgrade/upgrade-progress/upgrade-progress.component.html @@ -1,48 +1,15 @@ -
- -

- -

- -

- {{ executingTask?.description }} -

- -
{{ upgradeStatus.which }}
-
- -
-
- - Finished upgrading: - - {{ upgradeStatus.services_complete }} - - -
- -
- -

- - {{ executingTask?.progress || 0 }} % - -

-
-

{{ upgradeStatus.progress}}

- -
- {{ upgradeStatus.message }} -
+
+ + +
-
+
- - -

- -

- -

- {{ executingTask?.description }} -

-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html index 9476368f3e78f..e33c0dde43283 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html @@ -17,27 +17,25 @@ routerLink="/services"> Cluster->Services - - - - - - - - - - + + + + + + { - const realmName = data.map((item: { [x: string]: any }) => item['realm_name']); + const hasEmptyRealmName = data.some( + (item: { [x: string]: any }) => + item['realm_name'] === '' && + !data.some((i: { [x: string]: any }) => i['id'] === item['id'] && i['realm_name'] !== '') + ); if ( - this.defaultRealmId != '' && - this.defaultZonegroupId != '' && - this.defaultZoneId != '' && - realmName.includes('') + this.defaultRealmId !== '' && + this.defaultZonegroupId !== '' && + this.defaultZoneId !== '' && + hasEmptyRealmName ) { this.restartGatewayMessage = true; } @@ -431,18 +435,16 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit { defaultZonegroupId: string, defaultZoneId: string ): any { - const defaultRealm = this.realms.find((x: { id: string }) => x.id === defaultRealmId); - const defaultZonegroup = this.zonegroups.find( + const defaultRealm = this.realms?.find((x: { id: string }) => x.id === defaultRealmId); + const defaultZonegroup = this.zonegroups?.find( (x: { id: string }) => x.id === defaultZonegroupId ); - const defaultZone = this.zones.find((x: { id: string }) => x.id === defaultZoneId); - const defaultRealmName = defaultRealm !== undefined ? defaultRealm.name : null; - const defaultZonegroupName = defaultZonegroup !== undefined ? defaultZonegroup.name : null; - const defaultZoneName = defaultZone !== undefined ? defaultZone.name : null; + const defaultZone = this.zones?.find((x: { id: string }) => x.id === defaultZoneId); + return { - defaultRealmName: defaultRealmName, - defaultZonegroupName: defaultZonegroupName, - defaultZoneName: defaultZoneName + defaultRealmName: defaultRealm?.name, + defaultZonegroupName: defaultZonegroup?.name, + defaultZoneName: defaultZone?.name }; } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/multisite-wizard-steps.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/multisite-wizard-steps.enum.ts new file mode 100644 index 0000000000000..299ffee5b7318 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/multisite-wizard-steps.enum.ts @@ -0,0 +1,19 @@ +export enum StepTitles { + CreateRealmAndZonegroup = 'Create Realm & Zonegroup', + CreateZone = 'Create Zone', + SelectCluster = 'Select Cluster', + Review = 'Review' +} + +export const STEP_TITLES_MULTI_CLUSTER_CONFIGURED = [ + StepTitles.CreateRealmAndZonegroup, + StepTitles.CreateZone, + StepTitles.SelectCluster, + StepTitles.Review +]; + +export const STEP_TITLES_SINGLE_CLUSTER = [ + StepTitles.CreateRealmAndZonegroup, + StepTitles.CreateZone, + StepTitles.Review +]; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/rgw-multisite-wizard.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/rgw-multisite-wizard.component.html index 8956e3ef0b27f..9a34ce63bacbd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/rgw-multisite-wizard.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-wizard/rgw-multisite-wizard.component.html @@ -22,13 +22,19 @@ #formDir="ngForm" novalidate> - - Please note that this process can take some time. During this period, do not click the back button or close the wizard. Thank you for your patience. -
+ + This wizard enables you to set up multi-site replication within your + Ceph environment.If you have already added another cluster to your + multi-cluster setup, you can select that cluster in the wizard to + automate the replication process.If no additional cluster is currently + added, the wizard will guide you through creating the necessary realm, + zonegroup, and zone, and provide a realm token.This token can be used + later to manually import into a desired cluster to establish replication + between the clusters. +
-

Create Zone

-
-
-

Select Cluster

+
+ i18n>Replication Cluster
- - Name of the realm that will be involved in replication. - -
-
-
- -
- - - - - This field displays the token needed to import the multisite configuration into a secondary cluster. Copy this token securely and use it on the secondary cluster to replicate the current multisite setup. Ensure that the token is handled securely to prevent unauthorized access. - -
+
+ +
+ + + Replication zone represents the zone to be created in the replication cluster where your data will be replicated. + + This field is required.
-
- +
+
+
+
+ + + + + +
@@ -239,6 +228,12 @@
+