]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Display useful info if RGW is not configured 21785/head
authorVolker Theile <vtheile@suse.com>
Mon, 7 May 2018 14:50:14 +0000 (16:50 +0200)
committerVolker Theile <vtheile@suse.com>
Mon, 14 May 2018 10:58:29 +0000 (12:58 +0200)
Signed-off-by: Volker Theile <vtheile@suse.com>
qa/tasks/mgr/dashboard/test_rgw.py
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts

index 81eced2f6d0823c988d068fa7d661c46a5b1400d..0c73b142a4cf68492e10036bbd516ccb7b45bdee 100644 (file)
@@ -31,6 +31,23 @@ class RgwControllerTest(DashboardTestCase):
         self.assertIn('rgw_status', data)
         self.assertTrue(data['rgw_metadata'])
 
+    @authenticate
+    def test_rgw_status(self):
+        self._radosgw_admin_cmd([
+            'user', 'create', '--uid=admin', '--display-name=admin',
+            '--system', '--access-key=admin', '--secret=admin'
+        ])
+        self._ceph_cmd(['dashboard', 'set-rgw-api-user-id', 'admin'])
+        self._ceph_cmd(['dashboard', 'set-rgw-api-secret-key', 'admin'])
+        self._ceph_cmd(['dashboard', 'set-rgw-api-access-key', 'admin'])
+
+        data = self._get('/api/rgw/status')
+        self.assertStatus(200)
+        self.assertIn('available', data)
+        self.assertIn('message', data)
+        self.assertTrue(data['available'])
+
+
 class RgwProxyExceptionsTest(DashboardTestCase):
 
     @classmethod
index ae0de6305b1004c9077df2669e1223c8a5186e1f..13f177a60698f5992120903b4f7694985f24123a 100644 (file)
@@ -14,7 +14,27 @@ from ..rest_client import RequestException
 @ApiController('rgw')
 @AuthRequired()
 class Rgw(RESTController):
-    pass
+
+    @cherrypy.expose
+    @cherrypy.tools.json_out()
+    def status(self):
+        status = {'available': False, 'message': None}
+        try:
+            instance = RgwClient.admin_instance()
+            # Check if the service is online.
+            if not instance.is_service_online():
+                status['message'] = 'Failed to connect to the Object Gateway\'s Admin Ops API.'
+                raise RequestException(status['message'])
+            # If the API user ID is configured via 'ceph dashboard set-rgw-api-user-id <user_id>'
+            # (which is not mandatory), then ensure it is known by the RGW.
+            if instance.userid and not instance.is_system_user():
+                status['message'] = 'The user "{}" is unknown to the Object Gateway.'.format(
+                    instance.userid)
+                raise RequestException(status['message'])
+            status['available'] = True
+        except RequestException:
+            pass
+        return status
 
 
 @ApiController('rgw/daemon')
index a4fb77be41f101b34c774feb84020c11fd00115e..bca500fff68a0e9344f92821827da34e3152cd44 100644 (file)
@@ -16,6 +16,7 @@ import {
   PerformanceCounterComponent
 } from './ceph/performance-counter/performance-counter/performance-counter.component';
 import { PoolListComponent } from './ceph/pool/pool-list/pool-list.component';
+import { Rgw501Component } from './ceph/rgw/rgw-501/rgw-501.component';
 import { RgwBucketFormComponent } from './ceph/rgw/rgw-bucket-form/rgw-bucket-form.component';
 import { RgwBucketListComponent } from './ceph/rgw/rgw-bucket-list/rgw-bucket-list.component';
 import { RgwDaemonListComponent } from './ceph/rgw/rgw-daemon-list/rgw-daemon-list.component';
@@ -24,6 +25,7 @@ import { RgwUserListComponent } from './ceph/rgw/rgw-user-list/rgw-user-list.com
 import { LoginComponent } from './core/auth/login/login.component';
 import { NotFoundComponent } from './core/not-found/not-found.component';
 import { AuthGuardService } from './shared/services/auth-guard.service';
+import { ModuleStatusGuardService } from './shared/services/module-status-guard.service';
 
 const routes: Routes = [
   { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
@@ -31,40 +33,53 @@ const routes: Routes = [
   { path: 'hosts', component: HostsComponent, canActivate: [AuthGuardService] },
   { path: 'login', component: LoginComponent },
   { path: 'hosts', component: HostsComponent, canActivate: [AuthGuardService] },
+  { path: 'rgw/501/:message', component: Rgw501Component, canActivate: [AuthGuardService] },
   {
-    path: 'rgw/daemon',
-    component: RgwDaemonListComponent,
-    canActivate: [AuthGuardService]
-  },
-  {
-    path: 'rgw/user',
-    component: RgwUserListComponent,
-    canActivate: [AuthGuardService]
-  },
-  {
-    path: 'rgw/user/add',
-    component: RgwUserFormComponent,
-    canActivate: [AuthGuardService]
-  },
-  {
-    path: 'rgw/user/edit/:uid',
-    component: RgwUserFormComponent,
-    canActivate: [AuthGuardService]
-  },
-  {
-    path: 'rgw/bucket',
-    component: RgwBucketListComponent,
-    canActivate: [AuthGuardService]
-  },
-  {
-    path: 'rgw/bucket/add',
-    component: RgwBucketFormComponent,
-    canActivate: [AuthGuardService]
-  },
-  {
-    path: 'rgw/bucket/edit/:bucket',
-    component: RgwBucketFormComponent,
-    canActivate: [AuthGuardService]
+    path: 'rgw',
+    canActivateChild: [ModuleStatusGuardService],
+    data: {
+      moduleStatusGuardConfig: {
+        apiPath: 'rgw',
+        redirectTo: 'rgw/501'
+      }
+    },
+    children: [
+      {
+        path: 'daemon',
+        component: RgwDaemonListComponent,
+        canActivate: [AuthGuardService]
+      },
+      {
+        path: 'user',
+        component: RgwUserListComponent,
+        canActivate: [AuthGuardService]
+      },
+      {
+        path: 'user/add',
+        component: RgwUserFormComponent,
+        canActivate: [AuthGuardService]
+      },
+      {
+        path: 'user/edit/:uid',
+        component: RgwUserFormComponent,
+        canActivate: [AuthGuardService]
+      },
+      {
+        path: 'bucket',
+        component: RgwBucketListComponent,
+        canActivate: [AuthGuardService]
+      },
+      {
+        path: 'bucket/add',
+        component: RgwBucketFormComponent,
+        canActivate: [AuthGuardService]
+      },
+      {
+        path: 'bucket/edit/:bucket',
+        component: RgwBucketFormComponent,
+        canActivate: [AuthGuardService]
+      }
+    ]
   },
   { path: 'block/iscsi', component: IscsiComponent, canActivate: [AuthGuardService] },
   { path: 'block/rbd', component: RbdListComponent, canActivate: [AuthGuardService] },
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.html
new file mode 100644 (file)
index 0000000..da92b5d
--- /dev/null
@@ -0,0 +1,12 @@
+<nav aria-label="breadcrumb">
+  <ol class="breadcrumb">
+    <li i18n
+        class="breadcrumb-item">Object Gateway</li>
+  </ol>
+</nav>
+<cd-info-panel>
+  {{ message }}
+  <ng-container i18n>
+    Please consult the <a href="http://docs.ceph.com/docs/mimic/mgr/dashboard/#enabling-the-object-gateway-management-frontend">documentation</a> on how to configure and enable the Object Gateway management functionality.
+  </ng-container>
+</cd-info-panel>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.scss
new file mode 100644 (file)
index 0000000..f2cd387
--- /dev/null
@@ -0,0 +1,6 @@
+.alert-row-icon {
+  vertical-align: top;
+  padding-right: 15px;
+}
+.alert-row-text {
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.spec.ts
new file mode 100644 (file)
index 0000000..0336834
--- /dev/null
@@ -0,0 +1,31 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { SharedModule } from '../../../shared/shared.module';
+import { Rgw501Component } from './rgw-501.component';
+
+describe('Rgw501Component', () => {
+  let component: Rgw501Component;
+  let fixture: ComponentFixture<Rgw501Component>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ Rgw501Component ],
+      imports: [
+        RouterTestingModule,
+        SharedModule
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(Rgw501Component);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.ts
new file mode 100644 (file)
index 0000000..d25ac7b
--- /dev/null
@@ -0,0 +1,26 @@
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import * as _ from 'lodash';
+
+@Component({
+  selector: 'cd-rgw-501',
+  templateUrl: './rgw-501.component.html',
+  styleUrls: ['./rgw-501.component.scss']
+})
+export class Rgw501Component implements OnInit, OnDestroy {
+  message = 'The Object Gateway service is not configured.';
+  routeParamsSubscribe: any;
+
+  constructor(private route: ActivatedRoute) {}
+
+  ngOnInit() {
+    this.routeParamsSubscribe = this.route.params.subscribe((params: { message: string }) => {
+      this.message = params.message;
+    });
+  }
+
+  ngOnDestroy() {
+    this.routeParamsSubscribe.unsubscribe();
+  }
+}
index 90623dc98684da92bc457485ca5d00d336bc7979..8f82285bfad7c661030e6dade3c5f121b6802bfd 100644 (file)
@@ -2,11 +2,18 @@ import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
-import { BsDropdownModule, ModalModule, TabsModule, TooltipModule } from 'ngx-bootstrap';
+import {
+  AlertModule,
+  BsDropdownModule,
+  ModalModule,
+  TabsModule,
+  TooltipModule
+} from 'ngx-bootstrap';
 
 import { AppRoutingModule } from '../../app-routing.module';
 import { SharedModule } from '../../shared/shared.module';
 import { PerformanceCounterModule } from '../performance-counter/performance-counter.module';
+import { Rgw501Component } from './rgw-501/rgw-501.component';
 import { RgwBucketDetailsComponent } from './rgw-bucket-details/rgw-bucket-details.component';
 import { RgwBucketFormComponent } from './rgw-bucket-form/rgw-bucket-form.component';
 import { RgwBucketListComponent } from './rgw-bucket-list/rgw-bucket-list.component';
@@ -45,12 +52,14 @@ import {
     FormsModule,
     ReactiveFormsModule,
     PerformanceCounterModule,
+    AlertModule.forRoot(),
     BsDropdownModule.forRoot(),
     TabsModule.forRoot(),
     TooltipModule.forRoot(),
     ModalModule.forRoot()
   ],
   exports: [
+    Rgw501Component,
     RgwDaemonListComponent,
     RgwDaemonDetailsComponent,
     RgwBucketFormComponent,
@@ -60,6 +69,7 @@ import {
     RgwUserDetailsComponent
   ],
   declarations: [
+    Rgw501Component,
     RgwDaemonListComponent,
     RgwDaemonDetailsComponent,
     RgwBucketFormComponent,