forked from shaba/openuds
Advancing on deployed services
This commit is contained in:
parent
e925a2a356
commit
363f8939d9
@ -20,6 +20,7 @@ encoding//src/uds/REST/methods/osmanagers.py=utf-8
|
||||
encoding//src/uds/REST/methods/providers.py=utf-8
|
||||
encoding//src/uds/REST/methods/services.py=utf-8
|
||||
encoding//src/uds/REST/methods/transports.py=utf-8
|
||||
encoding//src/uds/REST/methods/user_services.py=utf-8
|
||||
encoding//src/uds/REST/methods/users_groups.py=utf-8
|
||||
encoding//src/uds/REST/model.py=utf-8
|
||||
encoding//src/uds/REST/processors.py=utf-8
|
||||
|
@ -48,7 +48,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class Authenticators(ModelHandler):
|
||||
model = Authenticator
|
||||
custom_methods = ['search']
|
||||
# Custom get method "search" that requires authenticator id
|
||||
custom_methods = [('search', True)]
|
||||
detail = { 'users': Users, 'groups':Groups }
|
||||
save_fields = ['name', 'comments', 'priority', 'small_name']
|
||||
|
||||
|
@ -35,16 +35,12 @@ from __future__ import unicode_literals
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
from uds.models import DeployedService, Service
|
||||
from uds.models import DeployedService
|
||||
from uds.core.util.State import State
|
||||
from uds.core.util import log
|
||||
from uds.core.Environment import Environment
|
||||
from uds.REST.model import ModelHandler
|
||||
from uds.REST import NotFound, ResponseError, RequestError
|
||||
from django.db import IntegrityError
|
||||
|
||||
from services import Services
|
||||
from osmanagers import OsManagers
|
||||
from uds.REST import NotFound
|
||||
from user_services import AssignedService, CachedService
|
||||
|
||||
import logging
|
||||
|
||||
@ -52,14 +48,18 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class DeployedServices(ModelHandler):
|
||||
model = DeployedService
|
||||
detail = { 'services': AssignedService, 'cache': CachedService }
|
||||
|
||||
save_fields = ['name', 'comments', 'service', 'osmanager', 'initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs']
|
||||
|
||||
table_title = _('Deployed services')
|
||||
table_fields = [
|
||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-laptop text-info' } },
|
||||
{ 'name': {'title': _('Name') } },
|
||||
{ 'parent': {'title': _('Parent Service') } }, # Will process this field on client in fact, not sent by server
|
||||
{ 'state': { 'title': _('state'), 'type': 'dict', 'dict': State.dictionary() } },
|
||||
{ 'comments': {'title': _('Comments')}},
|
||||
]
|
||||
# Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, ....
|
||||
table_row_style = { 'field': 'state', 'prefix': 'row-state-' }
|
||||
|
||||
def item_as_dict(self, item):
|
||||
|
@ -33,7 +33,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
from uds.models import Provider, UserService
|
||||
from uds.models import Provider, Service, UserService
|
||||
from services import Services as DetailServices
|
||||
from uds.core import services
|
||||
|
||||
@ -48,6 +48,8 @@ logger = logging.getLogger(__name__)
|
||||
class Providers(ModelHandler):
|
||||
model = Provider
|
||||
detail = { 'services': DetailServices }
|
||||
custom_methods = [('allservices', False)]
|
||||
|
||||
save_fields = ['name', 'comments']
|
||||
|
||||
table_title = _('Service providers')
|
||||
@ -95,3 +97,6 @@ class Providers(ModelHandler):
|
||||
raise NotFound('type not found')
|
||||
|
||||
|
||||
def allservices(self):
|
||||
for s in Service.objects.all():
|
||||
yield DetailServices.serviceToDict(s, True)
|
||||
|
@ -51,16 +51,31 @@ logger = logging.getLogger(__name__)
|
||||
class Services(DetailHandler):
|
||||
|
||||
@staticmethod
|
||||
def serviceToDict(item):
|
||||
return {
|
||||
def serviceToDict(item, full=False):
|
||||
retVal = {
|
||||
'id':item.id,
|
||||
'name': item.name,
|
||||
'comments': item.comments,
|
||||
'type': item.data_type,
|
||||
'typeName' : _(item.getType().name()),
|
||||
'type_name' : _(item.getType().name()),
|
||||
'deployed_services_count' : item.deployedServices.count(),
|
||||
'user_services_count': UserService.objects.filter(deployed_service__service=item).count(),
|
||||
}
|
||||
if full:
|
||||
info = item.getType()
|
||||
retVal['info'] = {
|
||||
'icon': info.icon().replace('\n', ''),
|
||||
'needs_publication': info.publicationType is not None,
|
||||
'max_deployed': info.maxDeployed,
|
||||
'uses_cache': info.usesCache,
|
||||
'uses_cache_l2': info.usesCache_L2,
|
||||
'cache_tooltip': _(info.cacheTooltip),
|
||||
'cache_tooltip_l2': _(info.cacheTooltip),
|
||||
'needs_manager': info.needsManager,
|
||||
'must_assign_manually': info.mustAssignManually,
|
||||
}
|
||||
|
||||
return retVal
|
||||
|
||||
def getItems(self, parent, item):
|
||||
# Extract provider
|
||||
@ -122,7 +137,7 @@ class Services(DetailHandler):
|
||||
return [
|
||||
{ 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } },
|
||||
{ 'comments': { 'title': _('Comments') } },
|
||||
{ 'type': {'title': _('Type') } },
|
||||
{ 'type_name': {'title': _('Type') } },
|
||||
{ 'deployed_services_count': {'title': _('Deployed services'), 'type': 'numeric', 'width': '7em'}},
|
||||
{ 'user_services_count': {'title': _('User services'), 'type': 'numeric', 'width': '7em'}},
|
||||
]
|
||||
|
121
server/src/uds/REST/methods/user_services.py
Normal file
121
server/src/uds/REST/methods/user_services.py
Normal file
@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
from uds.models import UserService
|
||||
from uds.core.util.State import State
|
||||
from uds.core.util import log
|
||||
from uds.core.Environment import Environment
|
||||
from uds.REST.model import DetailHandler
|
||||
from uds.REST import NotFound, ResponseError, RequestError
|
||||
from django.db import IntegrityError
|
||||
|
||||
from services import Services
|
||||
from osmanagers import OsManagers
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AssignedService(DetailHandler):
|
||||
|
||||
@staticmethod
|
||||
def itemToDict(item, is_cache=False):
|
||||
val = {
|
||||
'id' : item.id,
|
||||
'id_deployed_service' : item.deployed_service_id,
|
||||
'unique_id' : item.unique_id,
|
||||
'friendly_name' : item.friendly_name,
|
||||
'state' : item.state,
|
||||
'os_state': item.os_state,
|
||||
'state_date' : item.state_date,
|
||||
'creation_date' : item.creation_date,
|
||||
'revision' : item.publication and item.publication.revision or '',
|
||||
}
|
||||
|
||||
if is_cache:
|
||||
val['cacheLevel'] = item.cache_level
|
||||
else:
|
||||
val.update({
|
||||
'owner': item.user.manager.name + "-" + item.user.name,
|
||||
'in_use': item.in_use,
|
||||
'in_use_date': item.in_use_date,
|
||||
'source_host' : item.src_hostname,
|
||||
'source_ip': item.src_ip
|
||||
})
|
||||
return val
|
||||
|
||||
def getItems(self, parent, item):
|
||||
# Extract provider
|
||||
try:
|
||||
if item is None:
|
||||
return [AssignedService.itemToDict(k) for k in parent.assignedUserServices().all() ]
|
||||
else:
|
||||
return parent.assignedUserServices().get(pk=item)
|
||||
except:
|
||||
logger.exception('getItems')
|
||||
self.invalidItemException()
|
||||
|
||||
def getTitle(self, parent):
|
||||
try:
|
||||
return _('Assigned Services of {0}').format(parent.name)
|
||||
except:
|
||||
return _('Assigned services')
|
||||
|
||||
def getFields(self, parent):
|
||||
return [
|
||||
{ 'creation_date': { 'title': _('Creation date'), 'type': 'datetime' } },
|
||||
{ 'revision': { 'title': _('Revision') } },
|
||||
{ 'unique_id': { 'title': 'Unique ID'} },
|
||||
{ 'friendly_name': {'title': _('Friendly name')} },
|
||||
{ 'state': { 'title': _('State'), 'type': 'dict', 'dict': State.dictionary() } },
|
||||
{ 'owner': { 'title': _('Owner') } },
|
||||
]
|
||||
|
||||
|
||||
class CachedService(AssignedService):
|
||||
|
||||
def getItems(self, parent, item):
|
||||
# Extract provider
|
||||
try:
|
||||
if item is None:
|
||||
return [AssignedService.itemToDict(k, True) for k in parent.cachedUserServices().all() ]
|
||||
else:
|
||||
k = parent.cachedUserServices().get(pk=item)
|
||||
return AssignedService.itemToDict(k, True)
|
||||
except:
|
||||
logger.exception('getItems')
|
||||
self.invalidItemException()
|
@ -100,11 +100,9 @@ class Users(DetailHandler):
|
||||
# We need this fields for all
|
||||
logger.debug('Saving user {0} / {1}'.format(parent, item))
|
||||
valid_fields = ['name', 'real_name', 'comments', 'state', 'staff_member', 'is_admin']
|
||||
fields = self.readFieldsFromParams(valid_fields + ['groups'])
|
||||
fields = self.readFieldsFromParams(valid_fields)
|
||||
try:
|
||||
auth = parent.getInstance()
|
||||
groups = fields['groups']
|
||||
del fields['groups'] # Not update this on user dict
|
||||
if item is None: # Create new
|
||||
auth.createUser(fields) # this throws an exception if there is an error (for example, this auth can't create users)
|
||||
toSave = {}
|
||||
@ -120,6 +118,7 @@ class Users(DetailHandler):
|
||||
user.__dict__.update(toSave)
|
||||
|
||||
if auth.isExternalSource == False and user.parent == -1:
|
||||
groups = self.readFieldsFromParams(['groups'])['groups']
|
||||
user.groups = Group.objects.filter(id__in=groups)
|
||||
|
||||
user.save()
|
||||
|
@ -417,6 +417,25 @@ class ModelHandler(BaseModelHandler):
|
||||
result.append(res)
|
||||
return result
|
||||
|
||||
# if has custom methods, look for if this request matches any of them
|
||||
for cm in self.custom_methods:
|
||||
if nArgs > 1 and cm[1] is True: # Method needs parent (existing item)
|
||||
if self._args[1] == cm[0]:
|
||||
try:
|
||||
operation = getattr(self, self._args[1])
|
||||
item = self.model.objects.get(pk=self._args[0])
|
||||
except:
|
||||
self.invalidMethodException()
|
||||
|
||||
return operation(item)
|
||||
elif self._args[0] == cm[0]:
|
||||
try:
|
||||
operation = getattr(self, self._args[0])
|
||||
except:
|
||||
self.invalidMethodException()
|
||||
|
||||
return operation()
|
||||
|
||||
if nArgs == 1:
|
||||
if self._args[0] == OVERVIEW:
|
||||
return list(self.getItems())
|
||||
@ -456,16 +475,6 @@ class ModelHandler(BaseModelHandler):
|
||||
self.invalidItemException()
|
||||
return self.getLogs(item)
|
||||
|
||||
# if has custom methods
|
||||
if self._args[1] in self.custom_methods:
|
||||
try:
|
||||
operation = getattr(self, self._args[1])
|
||||
item = self.model.objects.get(pk=self._args[0])
|
||||
except:
|
||||
self.invalidMethodException()
|
||||
|
||||
return operation(item)
|
||||
|
||||
# If has detail and is requesting detail
|
||||
if self.detail is not None:
|
||||
return self.processDetail()
|
||||
|
@ -250,15 +250,6 @@ BasicModelRest.prototype = {
|
||||
});
|
||||
|
||||
},
|
||||
// Search
|
||||
search: function(id, type, term, success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
return this.get({
|
||||
id: id + '/search?type=' + encodeURIComponent(type) + '&term=' + encodeURIComponent(term),
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
},
|
||||
// -------------
|
||||
// Log methods
|
||||
// -------------
|
||||
@ -470,10 +461,31 @@ DetailModelRestApi.prototype = {
|
||||
// Populate api
|
||||
|
||||
api.providers = new BasicModelRest('providers');
|
||||
// all services method used in providers
|
||||
api.providers.allServices = function(success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
return this.get({
|
||||
id: 'allservices',
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// api.services = new BasicModelRest('services');
|
||||
api.authenticators = new BasicModelRest('authenticators');
|
||||
// Search method used in authenticators
|
||||
api.authenticators.search = function(id, type, term, success_fnc, fail_fnc) {
|
||||
"use strict";
|
||||
return this.get({
|
||||
id: id + '/search?type=' + encodeURIComponent(type) + '&term=' + encodeURIComponent(term),
|
||||
success: success_fnc,
|
||||
fail: fail_fnc
|
||||
});
|
||||
};
|
||||
|
||||
api.osmanagers = new BasicModelRest('osmanagers');
|
||||
api.transports = new BasicModelRest('transports');
|
||||
api.networks = new BasicModelRest('networks');
|
||||
api.deployedservices = new BasicModelRest('deployedservices');
|
||||
|
||||
|
@ -14,14 +14,14 @@ gui.authenticators.link = function(event) {
|
||||
|
||||
// Clears the log of the detail, in this case, the log of "users"
|
||||
// Memory saver :-)
|
||||
var detailLogTable;
|
||||
var detailLogTable = null;
|
||||
var clearDetailLog = function() {
|
||||
if( detailLogTable ) {
|
||||
var $tbl = $(detailLogTable).dataTable();
|
||||
$tbl.fnClearTable();
|
||||
$tbl.fnDestroy();
|
||||
$('#user-log-placeholder').empty();
|
||||
detailLogTable = undefined;
|
||||
detailLogTable = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4,37 +4,130 @@ gui.deployedservices = new GuiElement(api.deployedservices, 'deployedservices');
|
||||
|
||||
gui.deployedservices.link = function(event) {
|
||||
"use strict";
|
||||
gui.clearWorkspace();
|
||||
|
||||
api.templates.get('deployedservices', function(tmpl) {
|
||||
gui.clearWorkspace();
|
||||
gui.appendToWorkspace(api.templates.evaluate(tmpl, {
|
||||
deployed_services : 'deployed-services-placeholder',
|
||||
}));
|
||||
gui.setLinksEvents();
|
||||
|
||||
var testClick = function(val, value, btn, tbl, refreshFnc) {
|
||||
gui.doLog(value);
|
||||
};
|
||||
var counter = 0;
|
||||
var testSelect = function(val, value, btn, tbl, refreshFnc) {
|
||||
if( !val ) {
|
||||
$(btn).removeClass('btn3d-info').addClass('disabled');
|
||||
return;
|
||||
}
|
||||
$(btn).removeClass('disabled').addClass('btn3d-info');
|
||||
counter = counter + 1;
|
||||
gui.doLog('Select', counter.toString(), val, value);
|
||||
};
|
||||
// Clears the details
|
||||
// Memory saver :-)
|
||||
var prevTables = [];
|
||||
var clearDetails = function() {
|
||||
$.each(prevTables, function(undefined, tbl){
|
||||
var $tbl = $(tbl).dataTable();
|
||||
$tbl.fnClearTable();
|
||||
$tbl.fnDestroy();
|
||||
});
|
||||
|
||||
var tableId = gui.deployedservices.table({
|
||||
container : 'deployed-services-placeholder',
|
||||
rowSelect : 'single',
|
||||
buttons : [ 'new', 'edit', 'delete', { text: gettext('Test'), css: 'disabled', click: testClick, select: testSelect }, 'xls' ],
|
||||
onData: function(data) {
|
||||
gui.doLog(data);
|
||||
}
|
||||
$('#assigned-services-placeholder').empty();
|
||||
$('#cache-placeholder').empty();
|
||||
$('#transports-placeholder').empty();
|
||||
$('#groups-placeholder').empty();
|
||||
$('#logs-placeholder').empty();
|
||||
|
||||
$('#detail-placeholder').addClass('hidden');
|
||||
|
||||
prevTables = [];
|
||||
};
|
||||
|
||||
// Fills up the list of available services
|
||||
api.providers.allServices(function(services){
|
||||
var availableServices = {};
|
||||
|
||||
$.each(services, function(undefined, service){
|
||||
availableServices[service.id] = service;
|
||||
});
|
||||
|
||||
gui.doLog('Available services', availableServices);
|
||||
api.templates.get('deployedservices', function(tmpl) {
|
||||
gui.appendToWorkspace(api.templates.evaluate(tmpl, {
|
||||
deployed_services : 'deployed-services-placeholder',
|
||||
assigned_services : 'assigned-services-placeholder',
|
||||
cache : 'cache-placeholder',
|
||||
groups : 'groups-placeholder',
|
||||
transports : 'transports-placeholde',
|
||||
logs : 'logs-placeholder',
|
||||
}));
|
||||
gui.setLinksEvents();
|
||||
|
||||
var testClick = function(val, value, btn, tbl, refreshFnc) {
|
||||
gui.doLog(value);
|
||||
};
|
||||
var counter = 0;
|
||||
var testSelect = function(val, value, btn, tbl, refreshFnc) {
|
||||
if( !val ) {
|
||||
$(btn).removeClass('btn3d-info').addClass('disabled');
|
||||
return;
|
||||
}
|
||||
$(btn).removeClass('disabled').addClass('btn3d-info');
|
||||
counter = counter + 1;
|
||||
gui.doLog('Select', counter.toString(), val, value);
|
||||
};
|
||||
|
||||
var tableId = gui.deployedservices.table({
|
||||
container : 'deployed-services-placeholder',
|
||||
rowSelect : 'single',
|
||||
buttons : [ 'new', 'edit', 'delete', { text: gettext('Test'), css: 'disabled', click: testClick, select: testSelect }, 'xls' ],
|
||||
onRowDeselect: function() {
|
||||
clearDetails();
|
||||
},
|
||||
onRowSelect : function(selected) {
|
||||
var dps = selected[0];
|
||||
gui.doLog('Selected services pool', dps);
|
||||
|
||||
clearDetails();
|
||||
$('#detail-placeholder').removeClass('hidden');
|
||||
// If service does not supports cache, do not show it
|
||||
try {
|
||||
var service = availableServices[dps.service_id];
|
||||
} catch (e) {
|
||||
gui.doLog('Exception on rowSelect', e);
|
||||
gui.notify(gettext('Error processing deployed service'), 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
var cachedItems = null;
|
||||
// Shows/hides cache
|
||||
if( service.info.uses_cache || service.info.uses_cache_l2 ) {
|
||||
$('#cache-placeholder_tab').removeClass('hidden');
|
||||
cachedItems = api.deployedservices.detail(dps.id, 'cache');
|
||||
} else {
|
||||
$('#cache-placeholder_tab').addClass('hidden');
|
||||
}
|
||||
var groups = null;
|
||||
// Shows/hides groups
|
||||
if( service.info.must_assign_manually ) {
|
||||
$('#groups-placeholder_tab').removeClass('hidden');
|
||||
|
||||
} else {
|
||||
$('#groups-placeholder_tab').addClass('hidden');
|
||||
}
|
||||
|
||||
var assignedServices = new GuiElement(api.deployedservices.detail(dps.id, 'services'), 'services');
|
||||
var assignedServicesTable = assignedServices.table({
|
||||
container: 'assigned-services-placeholder',
|
||||
rowSelect: 'single',
|
||||
});
|
||||
|
||||
prevTables.push(assignedServicesTable);
|
||||
},
|
||||
// Preprocess data received to add "icon" to deployed service
|
||||
onData: function(data) {
|
||||
gui.doLog('onData', data);
|
||||
$.each(data, function(index, value){
|
||||
try {
|
||||
var service = availableServices[value.service_id];
|
||||
var style = 'display:inline-block; background: url(data:image/png;base64,' +
|
||||
service.info.icon + '); ' + 'width: 16px; height: 16px; vertical-align: middle;';
|
||||
|
||||
value.name = '<span style="' + style + '"></span> ' + value.name;
|
||||
|
||||
value.parent = service.name;
|
||||
} catch (e) {
|
||||
value.name = '<span class="fa fa-asterisk text-alert"></span> ' + value.name;
|
||||
value.parent = gettext('unknown (needs reload)');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
@ -4,10 +4,6 @@
|
||||
gui.osmanagers = new GuiElement(api.osmanagers, 'osm');
|
||||
gui.osmanagers.link = function(event) {
|
||||
"use strict";
|
||||
// Cleans up memory used by other datatables
|
||||
$.each($.fn.dataTable.fnTables(), function(undefined, tbl){
|
||||
$(tbl).dataTable().fnDestroy();
|
||||
});
|
||||
gui.clearWorkspace();
|
||||
gui.appendToWorkspace(gui.breadcrumbs('Os Managers'));
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
<li><a class="lnk-authenticators" href="#"><i class="fa fa-user"></i> {% trans 'Authenticators' %}</a></li>
|
||||
<li><a class="lnk-osmanagers" href="#"><i class="fa fa-edit"></i> Os Managers</a></li>
|
||||
<li><a class="lnk-connectivity" href="#"><i class="fa fa-font"></i> {% trans 'Connectivity' %}</a></li>
|
||||
<li><a class="lnk-deployed_services" href=""><i class="fa fa-desktop"></i> {% trans 'Deployed services' %}</a></li>
|
||||
<li><a class="lnk-deployed_services" href=""><i class="fa fa-desktop"></i> {% trans 'Services pool' %}</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-caret-square-o-down"></i> Tools <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
|
@ -15,22 +15,17 @@
|
||||
<div id="detail-placeholder" class="row hidden">
|
||||
<div class="col-xs-12">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#{{ users }}_tab" data-toggle="tab">{% endverbatim %}{% trans 'Users' %}{% verbatim %}</a></li>
|
||||
<li><a href="#{{ groups }}" data-toggle="tab">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</a></li>
|
||||
<li class="active"><a href="#{{ assigned_services }}" data-toggle="tab">{% endverbatim %}{% trans 'Assigned services' %}{% verbatim %}</a></li>
|
||||
<li id="{{ cache }}_tab"><a href="#{{ cache }}" data-toggle="tab">{% endverbatim %}{% trans 'Cache' %}{% verbatim %}</a></li>
|
||||
<li id="{{ groups }}_tab"><a href="#{{ groups }}" data-toggle="tab">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</a></li>
|
||||
<li><a href="#{{ transports }}" data-toggle="tab">{% endverbatim %}{% trans 'Transports' %}{% verbatim %}</a></li>
|
||||
<li><a href="#{{ logs }}" data-toggle="tab">{% endverbatim %}{% trans 'Logs' %}{% verbatim %}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade in active" id="{{ users }}_tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12" id="{{ users }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12" id="{{ users_log }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="{{ groups }}"></div>
|
||||
<div class="tab-pane fade in active" id="{{ assigned_services }}">...</div>
|
||||
<div class="tab-pane fade" id="{{ cache }}">...</div>
|
||||
<div class="tab-pane fade" id="{{ groups }}">...</div>
|
||||
<div class="tab-pane fade" id="{{ transports }}">...</div>
|
||||
<div class="tab-pane fade" id="{{ logs }}">...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user