]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: edit rgw-multisite
authoravanthakkar <avanjohn@gmail.com>
Mon, 6 Mar 2023 10:15:45 +0000 (15:45 +0530)
committerAashish Sharma <aasharma@redhat.com>
Mon, 27 Mar 2023 12:11:44 +0000 (17:41 +0530)
Fixes: https://tracker.ceph.com/issues/59171
Signed-off-by: Avan Thakkar <athakkar@redhat.com>
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-details/rgw-multisite-details.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-realm-form/rgw-multisite-realm-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-realm.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zone.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-zonegroup.service.ts
src/pybind/mgr/dashboard/openapi.yaml
src/pybind/mgr/dashboard/requirements-test.txt
src/pybind/mgr/dashboard/services/rgw_client.py

index 909bdecba5ad053ec2623ec55b4e4dfb0265bb79..d58dc309ea923c100704d3a02ec668af0e291aa0 100644 (file)
@@ -702,6 +702,16 @@ class RgwRealm(RESTController):
         except NoRgwDaemonsException as e:
             raise DashboardException(e, http_status_code=404, component='rgw')
 
+    @allow_empty_body
+    # pylint: disable=W0613
+    def set(self, realm_name, default, new_realm_name, daemon_name=None):
+        try:
+            instance = RgwClient.admin_instance()
+            result = instance.edit_realm(realm_name, default, new_realm_name)
+            return result
+        except NoRgwDaemonsException as e:
+            raise DashboardException(e, http_status_code=404, component='rgw')
+
 
 @APIRouter('/rgw/zonegroup', Scope.RGW)
 class RgwZonegroup(RESTController):
index 0e2e2accee8b12f1b7a29fdf8360cd75b7ec42fc..09e45a87e1b9a741ff9e34957e3ee0a48091220d 100644 (file)
                   <i [ngClass]="node.data.icon"></i>
                     {{ node.data.name }}
                 </span>
+                <span *ngIf="node.data.type"
+                      class="badge badge-info me-3">
+                    {{ node.data.type }}
+                </span>
                 <span class="badge badge-success me-2"
                       *ngIf="node.data.is_default">
                   default
                       *ngIf="node.data.is_master">
                   master
                 </span>
+                <div class="btn-group align-inline-btns"
+                     *ngIf="node.isFocused && node.data.type === 'realm'"
+                     role="group">
+                  <button type="button"
+                          [title]="title"
+                          class="btn btn-light dropdown-toggle-split ms-1"
+                          (click)="openModal(node, true)"
+                          ngbDropdownToggle>
+                    <i [ngClass]="[icons.edit]"></i>
+                  </button>
+                </div>
               </ng-template>
             </tree-root>
           </div>
index f11c00d56659afe5669cba3a863228e8dcbbc85b..25d994cc2cad1f810ef46178adb9aa685541c6f5 100644 (file)
@@ -103,7 +103,7 @@ export class RgwMultisiteDetailsComponent implements OnDestroy, OnInit {
   }
 
   openModal(entity: any, edit = false) {
-    const entityName = edit ? entity.data.name : entity;
+    const entityName = edit ? entity.data.type : entity;
     const action = edit ? 'edit' : 'create';
     const initialState = {
       resource: entityName,
index 18c52a56caa9b851e374d8389b9fded782bfc8f7..0d7d4e732324fe109eda173486f8927d8ffa20ba 100644 (file)
@@ -17,12 +17,14 @@ import { RgwRealm } from '../models/rgw-multisite';
 export class RgwMultisiteRealmFormComponent implements OnInit {
   action: string;
   multisiteRealmForm: CdFormGroup;
+  info: any;
   editing = false;
   resource: string;
   multisiteInfo: object[] = [];
   realm: RgwRealm;
   realmList: RgwRealm[] = [];
   realmNames: string[];
+  newRealmName: string;
 
   constructor(
     public activeModal: NgbActiveModal,
@@ -42,7 +44,11 @@ export class RgwMultisiteRealmFormComponent implements OnInit {
         validators: [
           Validators.required,
           CdValidators.custom('uniqueName', (realmName: string) => {
-            return this.realmNames && this.realmNames.indexOf(realmName) !== -1;
+            return (
+              this.action === 'create' &&
+              this.realmNames &&
+              this.realmNames.indexOf(realmName) !== -1
+            );
           })
         ]
       }),
@@ -58,23 +64,44 @@ export class RgwMultisiteRealmFormComponent implements OnInit {
     this.realmNames = this.realmList.map((realm) => {
       return realm['name'];
     });
+    if (this.action === 'edit') {
+      this.multisiteRealmForm.get('realmName').setValue(this.info.data.name);
+      this.multisiteRealmForm.get('default_realm').setValue(this.info.data.is_default);
+    }
   }
 
   submit() {
     const values = this.multisiteRealmForm.value;
     this.realm = new RgwRealm();
-    this.realm.name = values['realmName'];
-    this.rgwRealmService.create(this.realm, values['default_realm']).subscribe(
-      () => {
-        this.notificationService.show(
-          NotificationType.success,
-          $localize`Realm: '${values['realmName']}' created successfully`
-        );
-        this.activeModal.close();
-      },
-      () => {
-        this.multisiteRealmForm.setErrors({ cdSubmitButton: true });
-      }
-    );
+    if (this.action === 'create') {
+      this.realm.name = values['realmName'];
+      this.rgwRealmService.create(this.realm, values['default_realm']).subscribe(
+        () => {
+          this.notificationService.show(
+            NotificationType.success,
+            $localize`Realm: '${values['realmName']}' created successfully`
+          );
+          this.activeModal.close();
+        },
+        () => {
+          this.multisiteRealmForm.setErrors({ cdSubmitButton: true });
+        }
+      );
+    } else if (this.action === 'edit') {
+      this.realm.name = this.info.data.name;
+      this.newRealmName = values['realmName'];
+      this.rgwRealmService.update(this.realm, values['default_realm'], this.newRealmName).subscribe(
+        () => {
+          this.notificationService.show(
+            NotificationType.success,
+            $localize`Realm: '${values['realmName']}' updated successfully`
+          );
+          this.activeModal.close();
+        },
+        () => {
+          this.multisiteRealmForm.setErrors({ cdSubmitButton: true });
+        }
+      );
+    }
   }
 }
index db6733b293fe62d7aa6eb59b667c906cb4f95413..4c9c80c83f4eaeba2cc09c5038cbcc738f1d23a5 100644 (file)
@@ -23,6 +23,17 @@ export class RgwRealmService {
     });
   }
 
+  update(realm: RgwRealm, defaultRealm: boolean, newRealmName: string) {
+    return this.rgwDaemonService.request((params: HttpParams) => {
+      params = params.appendAll({
+        realm_name: realm.name,
+        default: defaultRealm,
+        new_realm_name: newRealmName
+      });
+      return this.http.put(`${this.url}/${realm.name}`, null, { params: params });
+    });
+  }
+
   list(): Observable<object> {
     return this.rgwDaemonService.request(() => {
       return this.http.get<object>(`${this.url}`);
@@ -46,10 +57,11 @@ export class RgwRealmService {
     let realmIds = [];
     nodes['id'] = realm.id;
     realmIds.push(realm.id);
-    nodes['name'] = realm.name + ' (realm)';
+    nodes['name'] = realm.name;
     nodes['info'] = realm;
     nodes['is_default'] = realm.id === defaultRealmId ? true : false;
     nodes['icon'] = Icons.reweight;
+    nodes['type'] = 'realm';
     return {
       nodes: nodes,
       realmIds: realmIds
index b6c1d360a7a2bcbf6a77b3793e88c32de74f945c..707154f247535eee16bedcbc92c66b76d177c2d8 100644 (file)
@@ -57,13 +57,14 @@ export class RgwZoneService {
     let zoneIds = [];
     nodes['id'] = zone.id;
     zoneIds.push(zone.id);
-    nodes['name'] = zone.name + ' (zone)';
+    nodes['name'] = zone.name;
     nodes['info'] = zone;
     nodes['icon'] = Icons.deploy;
     nodes['parent'] = zonegroup ? zonegroup.name : '';
     nodes['second_parent'] = realm ? realm.name : '';
     nodes['is_default'] = zone.id === defaultZoneId ? true : false;
     nodes['is_master'] = zonegroup && zonegroup.master_zone === zone.id ? true : false;
+    nodes['type'] = 'zone';
     return {
       nodes: nodes,
       zoneIds: zoneIds
index b8839fa93fda71c5e40b4fb865b101ce8235c63b..08242d1613b1e41d94076b131519294ac4e2f97b 100644 (file)
@@ -47,12 +47,13 @@ export class RgwZonegroupService {
   getZonegroupTree(zonegroup: RgwZonegroup, defaultZonegroupId: string, realm?: RgwRealm) {
     let nodes = {};
     nodes['id'] = zonegroup.id;
-    nodes['name'] = zonegroup.name + ' (zonegroup)';
+    nodes['name'] = zonegroup.name;
     nodes['info'] = zonegroup;
     nodes['icon'] = Icons.cubes;
     nodes['is_master'] = zonegroup.is_master;
     nodes['parent'] = realm ? realm.name : '';
     nodes['is_default'] = zonegroup.id === defaultZonegroupId ? true : false;
+    nodes['type'] = 'zonegroup';
     return nodes;
   }
 }
index 33031a94f13737049f475cffc10e1af213212621..a48600ff0e205aa5717298981f151a2a76dff72c 100644 (file)
@@ -8302,6 +8302,52 @@ paths:
       - jwt: []
       tags:
       - RgwRealm
+    put:
+      parameters:
+      - in: path
+        name: realm_name
+        required: true
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              properties:
+                daemon_name:
+                  type: string
+                default:
+                  type: string
+                new_realm_name:
+                  type: string
+              required:
+              - default
+              - new_realm_name
+              type: object
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource updated.
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      tags:
+      - RgwRealm
   /api/rgw/site:
     get:
       parameters:
index 4e925e8616f1ae63c5128278337c71aa7530bb92..d2566bab59f61a27474d227f62fa6f534fdd5697 100644 (file)
@@ -1,4 +1,4 @@
 pytest-cov
 pytest-instafail
 pyfakefs==4.5.0
-jsonschema==4.16.0
+jsonschema
index d4008faacb33c187a974bd03cb7218b797cb055c..5a022d0998e4597975dc695970e01377984deb4c 100644 (file)
@@ -17,7 +17,7 @@ from ..awsauth import S3Auth
 from ..exceptions import DashboardException
 from ..rest_client import RequestException, RestClient
 from ..settings import Settings
-from ..tools import dict_contains_path, dict_get, json_str_to_object
+from ..tools import dict_contains_path, dict_get, json_str_to_object, str_to_bool
 
 try:
     from typing import Any, Dict, List, Optional, Tuple, Union
@@ -660,6 +660,47 @@ class RgwClient(RestClient):
         except SubprocessError as error:
             raise DashboardException(error, http_status_code=500, component='rgw')
 
+    def edit_realm(self, realm_name: str, default: bool, new_realm_name: str):
+        rgw_realm_edit_cmd = []
+        cmd_edit_realm_options = []
+        if new_realm_name == realm_name:
+            if str_to_bool(default):
+                rgw_realm_edit_cmd = ['realm', 'default']
+                cmd_edit_realm_options = ['--rgw-realm', realm_name]
+                rgw_realm_edit_cmd += cmd_edit_realm_options
+                try:
+                    exit_code, _, err = mgr.send_rgwadmin_command(rgw_realm_edit_cmd)
+                    if exit_code > 0:
+                        raise DashboardException(e=err, msg='Unable to set {} as default realm'.format(realm_name),  # noqa E501  #pylint: disable=line-too-long
+                                                 http_status_code=500, component='rgw')
+                except SubprocessError as error:
+                    raise DashboardException(error, http_status_code=500, component='rgw')
+            else:
+                raise DashboardException(msg='The realm already exists',
+                                         http_status_code=400, component='rgw')
+        else:
+            rgw_realm_edit_cmd = ['realm', 'rename']
+            cmd_edit_realm_options = ['--rgw-realm', realm_name, '--realm-new-name', new_realm_name]
+            rgw_realm_edit_cmd += cmd_edit_realm_options
+            try:
+                exit_code, _, err = mgr.send_rgwadmin_command(rgw_realm_edit_cmd)
+                if exit_code > 0:
+                    raise DashboardException(e=err, msg='Unable to edit realm',
+                                             http_status_code=500, component='rgw')
+            except ValueError:
+                pass
+
+            if str_to_bool(default):
+                rgw_realm_edit_cmd = ['realm', 'default']
+                cmd_edit_realm_options = ['--rgw-realm', new_realm_name]
+                try:
+                    exit_code, _, _ = mgr.send_rgwadmin_command(rgw_realm_edit_cmd)
+                    if exit_code > 0:
+                        raise DashboardException(msg='Unable to set {} as default realm'.format(new_realm_name),  # noqa E501  #pylint: disable=line-too-long
+                                                 http_status_code=500, component='rgw')
+                except SubprocessError as error:
+                    raise DashboardException(error, http_status_code=500, component='rgw')
+
     def create_zonegroup(self, realm_name: str, zonegroup_name: str,
                          default: bool, master: bool, endpoints: List[str]):
         rgw_zonegroup_create_cmd = ['zonegroup', 'create']
@@ -686,7 +727,6 @@ class RgwClient(RestClient):
                                          http_status_code=500, component='rgw')
         except SubprocessError as error:
             raise DashboardException(error, http_status_code=500, component='rgw')
-        self.update_period()
         return out
 
     def list_zonegroups(self):
@@ -724,7 +764,7 @@ class RgwClient(RestClient):
                 for rgw_zonegroup in rgw_zonegroup_list['zonegroups']:
                     zonegroup_info = self.get_zonegroup(rgw_zonegroup)
                     zonegroups_info.append(zonegroup_info)
-                    all_zonegroups_info['zonegroups'] = zonegroups_info  # type: ignore
+                all_zonegroups_info['zonegroups'] = zonegroups_info  # type: ignore
             else:
                 all_zonegroups_info['zonegroups'] = []  # type: ignore
         if 'default_info' in rgw_zonegroup_list and rgw_zonegroup_list['default_info'] != '':