AUTH_ROLES = ['cluster-manager']
- URL_STATUS = '/api/orchestrator/status'
+ URL_STATUS = '/ui-api/orchestrator/status'
ORCHESTRATOR = True
# Set the default credentials.
self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-secret-key'], 'admin')
self._ceph_cmd_with_secret(['dashboard', 'set-rgw-api-access-key'], 'admin')
- data = self._get('/api/rgw/status')
+ data = self._get('/ui-api/rgw/status')
self.assertStatus(200)
self.assertIn('available', data)
self.assertIn('message', data)
self.assertTrue(data['rgw_metadata'])
def test_status(self):
- data = self._get('/api/rgw/status')
+ data = self._get('/ui-api/rgw/status')
self.assertStatus(200)
self.assertIn('available', data)
self.assertIn('message', data)
return composed_decorator
-@APIRouter('/nfs-ganesha', Scope.NFS_GANESHA)
-@APIDoc("NFS-Ganesha Cluster Management API", "NFS-Ganesha")
-class NFSGanesha(RESTController):
-
- @EndpointDoc("Status of NFS-Ganesha management feature",
- responses={200: {
- 'available': (bool, "Is API available?"),
- 'message': (str, "Error message")
- }})
- @Endpoint()
- @ReadPermission
- def status(self):
- status = {'available': True, 'message': None}
- try:
- mgr.remote('nfs', 'cluster_ls')
- except (ImportError, RuntimeError) as error:
- logger.exception(error)
- status['available'] = False
- status['message'] = str(error) # type: ignore
-
- return status
-
-
@APIRouter('/nfs-ganesha/cluster', Scope.NFS_GANESHA)
-@APIDoc(group="NFS-Ganesha")
+@APIDoc("NFS-Ganesha Cluster Management API", "NFS-Ganesha")
class NFSGaneshaCluster(RESTController):
@ReadPermission
@RESTController.MethodMap(version=APIVersion.EXPERIMENTAL)
@ReadPermission
def filesystems(self):
return CephFS.list_filesystems()
+
+ @Endpoint()
+ @ReadPermission
+ def status(self):
+ status = {'available': True, 'message': None}
+ try:
+ mgr.remote('nfs', 'cluster_ls')
+ except (ImportError, RuntimeError) as error:
+ logger.exception(error)
+ status['available'] = False
+ status['message'] = str(error) # type: ignore
+
+ return status
from ..exceptions import DashboardException
from ..services.orchestrator import OrchClient
-from . import APIDoc, APIRouter, Endpoint, EndpointDoc, ReadPermission, RESTController
+from . import APIDoc, Endpoint, EndpointDoc, ReadPermission, RESTController, UIRouter
STATUS_SCHEMA = {
"available": (bool, "Orchestrator status"),
return inner
-@APIRouter('/orchestrator')
+@UIRouter('/orchestrator')
@APIDoc("Orchestrator Management API", "Orchestrator")
class Orchestrator(RESTController):
format_features, get_image_spec, parse_image_spec, rbd_call, \
rbd_image_call
from ..tools import ViewCache, str_to_bool
-from . import APIDoc, APIRouter, CreatePermission, DeletePermission, \
- EndpointDoc, RESTController, Task, UpdatePermission, allow_empty_body
+from . import APIDoc, APIRouter, BaseController, CreatePermission, \
+ DeletePermission, Endpoint, EndpointDoc, ReadPermission, RESTController, \
+ Task, UIRouter, UpdatePermission, allow_empty_body
logger = logging.getLogger(__name__)
return rbd_call(pool_name, namespace, rbd_inst.trash_move, image_name, delay)
+@UIRouter('/block/rbd')
+class RbdStatus(BaseController):
+ @EndpointDoc("Display RBD Image feature status")
+ @Endpoint()
+ @ReadPermission
+ def status(self):
+ status = {'available': True, 'message': None}
+ if not CephService.get_pool_list('rbd'):
+ status['available'] = False
+ status['message'] = 'No RBD pools in the cluster. Please create a pool '\
+ 'with the "rbd" application label.' # type: ignore
+ return status
+
+
@APIRouter('/block/image/{image_spec}/snap', Scope.RBD_IMAGE)
@APIDoc("RBD Snapshot Management API", "RbdSnapshot")
class RbdSnapshot(RESTController):
from ..services.rgw_client import NoRgwDaemonsException, RgwClient
from ..tools import json_str_to_object, str_to_bool
from . import APIDoc, APIRouter, BaseController, Endpoint, EndpointDoc, \
- ReadPermission, RESTController, allow_empty_body
+ ReadPermission, RESTController, UIRouter, allow_empty_body
from ._version import APIVersion
try:
}
-@APIRouter('/rgw', Scope.RGW)
+@UIRouter('/rgw', Scope.RGW)
@APIDoc("RGW Management API", "Rgw")
class Rgw(BaseController):
@Endpoint()
--- /dev/null
+{ "available": false, "message": "No RBD pools in the cluster. Please create a pool with the \"rbd\" application label." }
\ No newline at end of file
{
menu: 'Block',
submenus: [
- { menu: 'Images', component: 'cd-rbd-list' },
+ { menu: 'Images', component: 'cd-error' },
{ menu: 'Mirroring', component: 'cd-mirroring' },
{ menu: 'iSCSI', component: 'cd-iscsi' }
]
}
checkNavigations(navs: any) {
- // The nfs-ganesha and RGW status requests are mocked to ensure that this method runs in time
- cy.intercept('/api/nfs-ganesha/status', { fixture: 'nfs-ganesha-status.json' });
- cy.intercept('/api/rgw/status', { fixture: 'rgw-status.json' });
+ // The nfs-ganesha, RGW, and block/rbd status requests are mocked to ensure that this method runs in time
+ cy.intercept('/ui-api/nfs-ganesha/status', { fixture: 'nfs-ganesha-status.json' });
+ cy.intercept('/ui-api/rgw/status', { fixture: 'rgw-status.json' });
+ cy.intercept('/ui-api/block/rbd/status', { fixture: 'block-rbd-status.json' });
navs.forEach((nav: any) => {
cy.contains('.simplebar-content li.nav-item a', nav.menu).click();
canActivate: [ModuleStatusGuardService],
data: {
moduleStatusGuardConfig: {
- apiPath: 'orchestrator',
+ uiApiPath: 'orchestrator',
redirectTo: 'dashboard',
backend: 'cephadm'
},
canActivate: [ModuleStatusGuardService],
data: {
moduleStatusGuardConfig: {
- apiPath: 'orchestrator',
+ uiApiPath: 'orchestrator',
redirectTo: 'error',
section: 'orch',
section_info: 'Orchestrator',
component: InventoryComponent,
data: {
moduleStatusGuardConfig: {
- apiPath: 'orchestrator',
+ uiApiPath: 'orchestrator',
redirectTo: 'error',
section: 'orch',
section_info: 'Orchestrator',
canActivateChild: [FeatureTogglesGuardService, ModuleStatusGuardService],
data: {
moduleStatusGuardConfig: {
- apiPath: 'rgw',
+ uiApiPath: 'rgw',
redirectTo: 'error',
section: 'rgw',
section_info: 'Object Gateway',
canActivateChild: [FeatureTogglesGuardService, ModuleStatusGuardService],
data: {
moduleStatusGuardConfig: {
- apiPath: 'nfs-ganesha',
+ uiApiPath: 'nfs-ganesha',
redirectTo: 'error',
section: 'nfs-ganesha',
section_info: 'NFS GANESHA',
import { ActionLabels, URLVerbs } from '~/app/shared/constants/app.constants';
import { FeatureTogglesGuardService } from '~/app/shared/services/feature-toggles-guard.service';
+import { ModuleStatusGuardService } from '~/app/shared/services/module-status-guard.service';
import { SharedModule } from '~/app/shared/shared.module';
import { IscsiSettingComponent } from './iscsi-setting/iscsi-setting.component';
import { IscsiTabsComponent } from './iscsi-tabs/iscsi-tabs.component';
{ path: '', redirectTo: 'rbd', pathMatch: 'full' },
{
path: 'rbd',
- canActivate: [FeatureTogglesGuardService],
- data: { breadcrumbs: 'Images' },
+ canActivate: [FeatureTogglesGuardService, ModuleStatusGuardService],
+ data: {
+ moduleStatusGuardConfig: {
+ uiApiPath: 'block/rbd',
+ redirectTo: 'error',
+ header: 'No RBD pools available',
+ button_name: 'Create RBD pool',
+ button_route: '/pool/create'
+ },
+ breadcrumbs: 'Images'
+ },
children: [
{ path: '', component: RbdListComponent },
{
the {{ section_info }} management functionality.</h4>
</div>
<br><br>
+ <div *ngIf="button_name && button_route; else dashboardButton">
+ <button class="btn btn-primary"
+ [routerLink]="button_route"
+ i18n>{{ button_name }}</button>
+ </div>
+ <ng-template #dashboardButton>
<div>
<button class="btn btn-primary"
[routerLink]="'/dashboard'"
i18n>Go To Dashboard</button>
</div>
+ </ng-template>
</div>
</div>
message: string;
section: string;
section_info: string;
+ button_name: string;
+ button_route: string;
icon: string;
docUrl: string;
source: string;
this.header = history.state.header;
this.section = history.state.section;
this.section_info = history.state.section_info;
+ this.button_name = history.state.button_name;
+ this.button_route = history.state.button_route;
this.icon = history.state.icon;
this.source = history.state.source;
this.docUrl = this.docService.urlGenerator(this.section);
describe('OrchestratorService', () => {
let service: OrchestratorService;
let httpTesting: HttpTestingController;
- const apiPath = 'api/orchestrator';
+ const uiApiPath = 'ui-api/orchestrator';
configureTestBed({
providers: [OrchestratorService],
it('should call status', () => {
service.status().subscribe();
- const req = httpTesting.expectOne(`${apiPath}/status`);
+ const req = httpTesting.expectOne(`${uiApiPath}/status`);
expect(req.request.method).toBe('GET');
});
});
providedIn: 'root'
})
export class OrchestratorService {
- private url = 'api/orchestrator';
+ private url = 'ui-api/orchestrator';
disableMessages = {
noOrchestrator: $localize`The feature is disabled because Orchestrator is not available.`,
route.url = [];
route.data = {
moduleStatusGuardConfig: {
- apiPath: 'bar',
+ uiApiPath: 'bar',
redirectTo: '/foo',
backend: 'rook'
}
/**
* This service checks if a route can be activated by executing a
- * REST API call to '/api/<apiPath>/status'. If the returned response
+ * REST API call to '/ui-api/<uiApiPath>/status'. If the returned response
* states that the module is not available, then the user is redirected
* to the specified <redirectTo> URL path.
*
* canActivate: [AuthGuardService, ModuleStatusGuardService],
* data: {
* moduleStatusGuardConfig: {
- * apiPath: 'rgw',
+ * uiApiPath: 'rgw',
* redirectTo: 'rgw/501'
* }
* }
}
);
}
- return this.http.get(`api/${config.apiPath}/status`).pipe(
+ return this.http.get(`ui-api/${config.uiApiPath}/status`).pipe(
map((resp: any) => {
if (!resp.available && !backendCheck) {
this.router.navigate([config.redirectTo || ''], {
message: resp.message,
section: config.section,
section_info: config.section_info,
+ button_name: config.button_name,
+ button_route: config.button_route,
icon: Icons.wrench
}
});
summary: Updates an NFS-Ganesha export
tags:
- NFS-Ganesha
- /api/nfs-ganesha/status:
- get:
- parameters: []
- responses:
- '200':
- content:
- application/vnd.ceph.api.v1.0+json:
- schema:
- properties:
- available:
- description: Is API available?
- type: boolean
- message:
- description: Error message
- type: string
- required:
- - available
- - message
- type: object
- description: OK
- '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: []
- summary: Status of NFS-Ganesha management feature
- tags:
- - NFS-Ganesha
- /api/orchestrator/status:
- get:
- parameters: []
- responses:
- '200':
- content:
- application/vnd.ceph.api.v1.0+json:
- schema:
- properties:
- available:
- description: Orchestrator status
- type: boolean
- message:
- description: Error message
- type: string
- required:
- - available
- - message
- type: object
- description: OK
- '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: []
- summary: Display Orchestrator Status
- tags:
- - Orchestrator
/api/osd:
get:
parameters: []
- jwt: []
tags:
- RgwSite
- /api/rgw/status:
- get:
- parameters: []
- responses:
- '200':
- content:
- application/vnd.ceph.api.v1.0+json:
- schema:
- properties:
- available:
- description: Is RGW available?
- type: boolean
- message:
- description: Descriptions
- type: string
- required:
- - available
- - message
- type: object
- description: OK
- '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: []
- summary: Display RGW Status
- tags:
- - Rgw
/api/rgw/user:
get:
parameters:
name: NFS-Ganesha
- description: OSD management API
name: OSD
-- description: Orchestrator Management API
- name: Orchestrator
- description: OSD Perf Counters Management API
name: OsdPerfCounter
- description: Perf Counters Management API
name: RbdTrash
- description: Feedback API
name: Report
-- description: RGW Management API
- name: Rgw
- description: RGW Bucket Management API
name: RgwBucket
- description: RGW Daemon Management API
from ..controllers.cephfs import CephFS
from ..controllers.iscsi import Iscsi, IscsiTarget
-from ..controllers.nfs import NFSGanesha, NFSGaneshaExports
+from ..controllers.nfs import NFSGaneshaExports, NFSGaneshaUi
from ..controllers.rbd import Rbd, RbdSnapshot, RbdTrash
from ..controllers.rbd_mirroring import RbdMirroringPoolMode, \
RbdMirroringPoolPeer, RbdMirroringSummary
Features.ISCSI: [Iscsi, IscsiTarget],
Features.CEPHFS: [CephFS],
Features.RGW: [Rgw, RgwDaemon, RgwBucket, RgwUser],
- Features.NFS: [NFSGanesha, NFSGaneshaExports],
+ Features.NFS: [NFSGaneshaUi, NFSGaneshaExports],
}
from .. import mgr
from ..controllers._version import APIVersion
-from ..controllers.nfs import NFSGanesha, NFSGaneshaExports, NFSGaneshaUi
+from ..controllers.nfs import NFSGaneshaExports, NFSGaneshaUi
from ..tests import ControllerTestCase
from ..tools import NotificationQueue, TaskManager
self.assertStatus(200)
self.assertJsonBody({'paths': []})
-
-class NFSGaneshaControllerTest(ControllerTestCase):
- @classmethod
- def setup_server(cls):
- cls.setup_controllers([NFSGanesha])
-
def test_status_available(self):
- self._get('/api/nfs-ganesha/status')
+ self._get('/ui-api/nfs-ganesha/status')
self.assertStatus(200)
self.assertJsonBody({'available': True, 'message': None})
def test_status_not_available(self):
mgr.remote = Mock(side_effect=RuntimeError('Test'))
- self._get('/api/nfs-ganesha/status')
+ self._get('/ui-api/nfs-ganesha/status')
self.assertStatus(200)
self.assertJsonBody({'available': False, 'message': 'Test'})
class OrchestratorControllerTest(ControllerTestCase):
- URL_STATUS = '/api/orchestrator/status'
+ URL_STATUS = '/ui-api/orchestrator/status'
URL_INVENTORY = '/api/orchestrator/inventory'
@classmethod
@patch.object(RgwClient, 'is_service_online', Mock(return_value=True))
@patch.object(RgwClient, '_is_system_user', Mock(return_value=True))
def test_status_available(self):
- self._get('/test/api/rgw/status')
+ self._get('/test/ui-api/rgw/status')
self.assertStatus(200)
self.assertJsonBody({'available': True, 'message': None})
@patch.object(RgwClient, 'is_service_online', Mock(
side_effect=RequestException('My test error')))
def test_status_online_check_error(self):
- self._get('/test/api/rgw/status')
+ self._get('/test/ui-api/rgw/status')
self.assertStatus(200)
self.assertJsonBody({'available': False,
'message': 'My test error'})
@patch.object(RgwClient, '_get_user_id', Mock(return_value='fake-user'))
@patch.object(RgwClient, 'is_service_online', Mock(return_value=False))
def test_status_not_online(self):
- self._get('/test/api/rgw/status')
+ self._get('/test/ui-api/rgw/status')
self.assertStatus(200)
self.assertJsonBody({'available': False,
'message': "Failed to connect to the Object Gateway's Admin Ops API."})
@patch.object(RgwClient, 'is_service_online', Mock(return_value=True))
@patch.object(RgwClient, '_is_system_user', Mock(return_value=False))
def test_status_not_system_user(self):
- self._get('/test/api/rgw/status')
+ self._get('/test/ui-api/rgw/status')
self.assertStatus(200)
self.assertJsonBody({'available': False,
'message': 'The system flag is not set for user "fake-user".'})
def test_status_no_service(self):
RgwStub.get_mgr_no_services()
- self._get('/test/api/rgw/status')
+ self._get('/test/ui-api/rgw/status')
self.assertStatus(200)
self.assertJsonBody({'available': False, 'message': 'No RGW service is running.'})